Compare commits
120 Commits
v1.2
...
sg2042-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc7ba17f3 | ||
|
|
6eef399cfc | ||
|
|
506f47f9fa | ||
|
|
3745939ceb | ||
|
|
44fdaafcb4 | ||
|
|
a82a033c74 | ||
|
|
270f27b81b | ||
|
|
f89d688f60 | ||
|
|
4826e4f743 | ||
|
|
d06c0a7858 | ||
|
|
d21bf89240 | ||
|
|
9af30e56df | ||
|
|
d0b705e19c | ||
|
|
546bc50f9a | ||
|
|
4aab6f3263 | ||
|
|
e1d3b68eeb | ||
|
|
19622b6c93 | ||
|
|
60142dc80c | ||
|
|
31087f36d7 | ||
|
|
58f9b858c5 | ||
|
|
be6e558f66 | ||
|
|
0e87f899c3 | ||
|
|
0b5c942ee9 | ||
|
|
64fb41e927 | ||
|
|
b210bc000c | ||
|
|
35fb234d39 | ||
|
|
c6a092cd80 | ||
|
|
e8e9ed3790 | ||
|
|
d56049e299 | ||
|
|
c631a7da27 | ||
|
|
57d3aa3b0d | ||
|
|
641d2e9f38 | ||
|
|
b51ddffcc0 | ||
|
|
548e4b4b28 | ||
|
|
60c358e677 | ||
|
|
51951d9e9a | ||
|
|
1fe8dc9955 | ||
|
|
506144f398 | ||
|
|
568ea49490 | ||
|
|
e9d08bd99c | ||
|
|
4b28afc98b | ||
|
|
908be1b85c | ||
|
|
5ccebf0a7e | ||
|
|
37558dccbe | ||
|
|
7c964e279c | ||
|
|
c9917b6108 | ||
|
|
73623a0aca | ||
|
|
8a40306371 | ||
|
|
07673fc063 | ||
|
|
b1ae6ef33b | ||
|
|
c88e039ec2 | ||
|
|
40f16a81d3 | ||
|
|
1364d5adb2 | ||
|
|
17b3776c81 | ||
|
|
bc06ff65bf | ||
|
|
5a75f5309c | ||
|
|
67b2a40892 | ||
|
|
2491242282 | ||
|
|
c10095132a | ||
|
|
31b82e0d50 | ||
|
|
81adc62f45 | ||
|
|
30ea8069f4 | ||
|
|
4f2be40102 | ||
|
|
6861ee996c | ||
|
|
99d09b601e | ||
|
|
745aaecc64 | ||
|
|
aafcc90a87 | ||
|
|
321293c644 | ||
|
|
65c2190b47 | ||
|
|
29285aead0 | ||
|
|
c43903c4ea | ||
|
|
5a41a3884f | ||
|
|
eab48c33a1 | ||
|
|
4e0572f57b | ||
|
|
0ee3a86fed | ||
|
|
e3bf1afcc5 | ||
|
|
aa5dafcb5b | ||
|
|
b224ddb41f | ||
|
|
680bea02bf | ||
|
|
09b34d8cca | ||
|
|
642f3de9b9 | ||
|
|
66b0e23a0c | ||
|
|
199189bd1c | ||
|
|
84d15f4f52 | ||
|
|
8050081f68 | ||
|
|
c8ea836ee3 | ||
|
|
c45992cc2b | ||
|
|
33bf917460 | ||
|
|
dea0922f86 | ||
|
|
230278dcf1 | ||
|
|
b666760bfa | ||
|
|
c10e3fe5f9 | ||
|
|
2f40a99c9e | ||
|
|
fefa548803 | ||
|
|
a990309fa3 | ||
|
|
7aaeeab9e7 | ||
|
|
787296ae92 | ||
|
|
9c4eb3521e | ||
|
|
b1818ee244 | ||
|
|
8ecbe6d3fb | ||
|
|
ce2a834c98 | ||
|
|
da5594bf85 | ||
|
|
001106d19b | ||
|
|
59a08cd7d6 | ||
|
|
3e2f573e70 | ||
|
|
20646e0184 | ||
|
|
44f736c96e | ||
|
|
1ac14f10f6 | ||
|
|
22dbdb3d60 | ||
|
|
aace1e145d | ||
|
|
9e0ba09076 | ||
|
|
9e397e3960 | ||
|
|
6997552ea2 | ||
|
|
8020df8733 | ||
|
|
cb7e7c3325 | ||
|
|
6957ae0e91 | ||
|
|
f14595a7cf | ||
|
|
65638f8d6b | ||
|
|
6509127ad6 | ||
|
|
440fa818fb |
18
Makefile
18
Makefile
@@ -254,6 +254,7 @@ deps-y=$(platform-objs-path-y:.o=.dep)
|
||||
deps-y+=$(libsbi-objs-path-y:.o=.dep)
|
||||
deps-y+=$(libsbiutils-objs-path-y:.o=.dep)
|
||||
deps-y+=$(firmware-objs-path-y:.o=.dep)
|
||||
deps-y+=$(firmware-elfs-path-y:=.dep)
|
||||
|
||||
# Setup platform ABI, ISA and Code Model
|
||||
ifndef PLATFORM_RISCV_ABI
|
||||
@@ -330,7 +331,12 @@ GENFLAGS += $(libsbiutils-genflags-y)
|
||||
GENFLAGS += $(platform-genflags-y)
|
||||
GENFLAGS += $(firmware-genflags-y)
|
||||
|
||||
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing -O2
|
||||
CFLAGS = -g -Wall -Werror -ffreestanding -nostdlib -fno-stack-protector -fno-strict-aliasing
|
||||
ifneq ($(DEBUG),)
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
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)
|
||||
@@ -369,7 +375,7 @@ ASFLAGS += $(firmware-asflags-y)
|
||||
ARFLAGS = rcs
|
||||
|
||||
ELFFLAGS += $(USE_LD_FLAG)
|
||||
ELFFLAGS += -Wl,--build-id=none -Wl,-N
|
||||
ELFFLAGS += -Wl,--build-id=none
|
||||
ELFFLAGS += $(platform-ldflags-y)
|
||||
ELFFLAGS += $(firmware-ldflags-y)
|
||||
|
||||
@@ -413,6 +419,11 @@ inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \
|
||||
inst_header_dir = $(CMD_PREFIX)mkdir -p $(1); \
|
||||
echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \
|
||||
cp -rf $(2) $(1)
|
||||
compile_cpp_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " CPP-DEP $(subst $(build_dir)/,,$(1))"; \
|
||||
printf %s `dirname $(1)`/ > $(1) && \
|
||||
$(CC) $(CPPFLAGS) -x c -MM $(3) \
|
||||
-MT `basename $(1:.dep=$(2))` >> $(1) || rm -f $(1)
|
||||
compile_cpp = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " CPP $(subst $(build_dir)/,,$(1))"; \
|
||||
$(CPP) $(CPPFLAGS) -x c $(2) | grep -v "\#" > $(1)
|
||||
@@ -543,6 +554,9 @@ $(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
|
||||
$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
|
||||
$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
|
||||
|
||||
$(platform_build_dir)/%.dep: $(src_dir)/%.ldS $(KCONFIG_CONFIG)
|
||||
$(call compile_cpp_dep,$@,.ld,$<)
|
||||
|
||||
$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
|
||||
$(call compile_cpp,$@,$<)
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -298,6 +298,19 @@ NOTE: Using `BUILD_INFO=y` without specifying SOURCE_DATE_EPOCH will violate
|
||||
purpose, and should NOT be used in a product which follows "reproducible
|
||||
builds".
|
||||
|
||||
Building with optimization off for debugging
|
||||
--------------------------------------------
|
||||
|
||||
When debugging OpenSBI, we may want to turn off the compiler optimization and
|
||||
make debugging produce the expected results for a better debugging experience.
|
||||
To build with optimization off we can just simply add `DEBUG=1`, like:
|
||||
```
|
||||
make DEBUG=1
|
||||
```
|
||||
|
||||
This definition is ONLY for development and debug purpose, and should NOT be
|
||||
used in a product build.
|
||||
|
||||
Contributing to OpenSBI
|
||||
-----------------------
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ has following details:
|
||||
* **next_mode** - Privilege mode of the next booting stage for this
|
||||
domain. This can be either S-mode or U-mode.
|
||||
* **system_reset_allowed** - Is domain allowed to reset the system?
|
||||
* **system_suspend_allowed** - Is domain allowed to suspend the system?
|
||||
|
||||
The memory regions represented by **regions** in **struct sbi_domain** have
|
||||
following additional constraints to align with RISC-V PMP requirements:
|
||||
@@ -91,6 +92,7 @@ following manner:
|
||||
* **next_mode** - Next booting stage mode in coldboot HART scratch space
|
||||
is the next mode for the ROOT domain
|
||||
* **system_reset_allowed** - The ROOT domain is allowed to reset the system
|
||||
* **system_suspend_allowed** - The ROOT domain is allowed to suspend the system
|
||||
|
||||
Domain Effects
|
||||
--------------
|
||||
@@ -124,6 +126,9 @@ The DT properties of a domain configuration DT node are as follows:
|
||||
* **compatible** (Mandatory) - The compatible string of the domain
|
||||
configuration. This DT property should have value *"opensbi,domain,config"*
|
||||
|
||||
* **system-suspend-test** (Optional) - When present, enable a system
|
||||
suspend test implementation which simply waits five seconds and issues a WFI.
|
||||
|
||||
### Domain Memory Region Node
|
||||
|
||||
The domain memory region DT node describes details of a memory region and
|
||||
@@ -160,8 +165,16 @@ The DT properties of a domain instance DT node are as follows:
|
||||
* **regions** (Optional) - The list of domain memory region DT node phandle
|
||||
and access permissions for the domain instance. Each list entry is a pair
|
||||
of DT node phandle and access permissions. The access permissions are
|
||||
represented as a 32bit bitmask having bits: **readable** (BIT[0]),
|
||||
**writeable** (BIT[1]), **executable** (BIT[2]), and **m-mode** (BIT[3]).
|
||||
represented as a 32bit bitmask having bits: **M readable** (BIT[0]),
|
||||
**M writeable** (BIT[1]), **M executable** (BIT[2]), **SU readable**
|
||||
(BIT[3]), **SU writable** (BIT[4]), and **SU executable** (BIT[5]).
|
||||
The enforce permission bit (BIT[6]), if set, will lock the permissions
|
||||
in the PMP. This will enforce the permissions on M-mode as well which
|
||||
otherwise will have unrestricted access. This bit must be used with
|
||||
caution because no changes can be made to a PMP entry once its locked
|
||||
until the hart is reset.
|
||||
Any region of a domain defined in DT node cannot have only M-bits set
|
||||
in access permissions i.e. it cannot be an m-mode only accessible region.
|
||||
* **boot-hart** (Optional) - The DT node phandle of the HART booting the
|
||||
domain instance. If coldboot HART is assigned to the domain instance then
|
||||
this DT property is ignored and the coldboot HART is assumed to be the
|
||||
@@ -180,13 +193,15 @@ The DT properties of a domain instance DT node are as follows:
|
||||
is used as default value.
|
||||
* **next-mode** (Optional) - The 32 bit next booting stage mode for the
|
||||
domain instance. The possible values of this DT property are: **0x1**
|
||||
(s-mode), and **0x0** (u-mode). If this DT property is not available
|
||||
(S-mode), and **0x0** (U-mode). If this DT property is not available
|
||||
and coldboot HART is not assigned to the domain instance then **0x1**
|
||||
is used as default value. If this DT property is not available and
|
||||
coldboot HART is assigned to the domain instance then **next booting
|
||||
stage mode of coldboot HART** is used as default value.
|
||||
* **system-reset-allowed** (Optional) - A boolean flag representing
|
||||
whether the domain instance is allowed to do system reset.
|
||||
* **system-suspend-allowed** (Optional) - A boolean flag representing
|
||||
whether the domain instance is allowed to do system suspend.
|
||||
|
||||
### Assigning HART To Domain Instance
|
||||
|
||||
@@ -195,9 +210,9 @@ platform support can provide the HART to domain instance assignment using
|
||||
platform specific callback.
|
||||
|
||||
The HART to domain instance assignment can be parsed from the device tree
|
||||
using optional DT property **opensbi,domain** in each CPU DT node. The
|
||||
value of DT property **opensbi,domain** is the DT phandle of the domain
|
||||
instance DT node. If **opensbi,domain** DT property is not specified then
|
||||
using optional DT property **opensbi-domain** in each CPU DT node. The
|
||||
value of DT property **opensbi-domain** is the DT phandle of the domain
|
||||
instance DT node. If **opensbi-domain** DT property is not specified then
|
||||
corresponding HART is assigned to **the ROOT domain**.
|
||||
|
||||
### Domain Configuration Only Accessible to OpenSBI
|
||||
@@ -222,6 +237,7 @@ be done:
|
||||
chosen {
|
||||
opensbi-domains {
|
||||
compatible = "opensbi,domain,config";
|
||||
system-suspend-test;
|
||||
|
||||
tmem: tmem {
|
||||
compatible = "opensbi,domain,memregion";
|
||||
@@ -246,18 +262,19 @@ be done:
|
||||
tdomain: trusted-domain {
|
||||
compatible = "opensbi,domain,instance";
|
||||
possible-harts = <&cpu0>;
|
||||
regions = <&tmem 0x7>, <&tuart 0x7>;
|
||||
regions = <&tmem 0x3f>, <&tuart 0x3f>;
|
||||
boot-hart = <&cpu0>;
|
||||
next-arg1 = <0x0 0x0>;
|
||||
next-addr = <0x0 0x80100000>;
|
||||
next-mode = <0x0>;
|
||||
system-reset-allowed;
|
||||
system-suspend-allowed;
|
||||
};
|
||||
|
||||
udomain: untrusted-domain {
|
||||
compatible = "opensbi,domain,instance";
|
||||
possible-harts = <&cpu1 &cpu2 &cpu3 &cpu4>;
|
||||
regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x7>;
|
||||
regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x3f>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ Firmware Configuration and Compilation
|
||||
All firmware types support the following common compile time configuration
|
||||
parameters:
|
||||
|
||||
* **FW_TEXT_ADDR** - Defines the execution address of the OpenSBI firmware.
|
||||
* **FW_TEXT_START** - Defines the execution address of the OpenSBI firmware.
|
||||
This configuration parameter is mandatory.
|
||||
* **FW_FDT_PATH** - Path to an external flattened device tree binary file to
|
||||
be embedded in the *.rodata* section of the final firmware. If this option
|
||||
|
||||
@@ -53,7 +53,7 @@ RISC-V Platforms Using Generic Platform
|
||||
* **Spike** (*[spike.md]*)
|
||||
* **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*)
|
||||
|
||||
[andes-ae350.md]: andse-ae350.md
|
||||
[andes-ae350.md]: andes-ae350.md
|
||||
[qemu_virt.md]: qemu_virt.md
|
||||
[renesas-rzfive.md]: renesas-rzfive.md
|
||||
[shakti_cclass.md]: shakti_cclass.md
|
||||
|
||||
@@ -298,6 +298,12 @@ _scratch_init:
|
||||
sub a5, t3, a4
|
||||
REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp)
|
||||
REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp)
|
||||
|
||||
/* Store R/W section's offset in scratch space */
|
||||
lla a4, __fw_rw_offset
|
||||
REG_L a5, 0(a4)
|
||||
REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp)
|
||||
|
||||
/* Store next arg1 in scratch space */
|
||||
MOV_3R s0, a0, s1, a1, s2, a2
|
||||
call fw_next_arg1
|
||||
@@ -422,9 +428,15 @@ _start_warm:
|
||||
li ra, 0
|
||||
call _reset_regs
|
||||
|
||||
/* Disable and clear all interrupts */
|
||||
/* Disable all interrupts */
|
||||
csrw CSR_MIE, zero
|
||||
csrw CSR_MIP, zero
|
||||
/*
|
||||
* Only clear the MIP_SSIP and MIP_STIP. For the platform like QEMU,
|
||||
* If we clear other interrupts like MIP_SEIP and the pendings of
|
||||
* PLIC still exist, the QEMU may not set it back immediately.
|
||||
*/
|
||||
li t0, (MIP_SSIP | MIP_STIP)
|
||||
csrc CSR_MIP, t0
|
||||
|
||||
/* Find HART count and HART stack size */
|
||||
lla a4, platform
|
||||
@@ -455,7 +467,7 @@ _start_warm:
|
||||
blt a4, s7, 1b
|
||||
li a4, -1
|
||||
2: add s6, a4, zero
|
||||
3: bge s6, s7, _start_hang
|
||||
3: bgeu s6, s7, _start_hang
|
||||
|
||||
/* Find the scratch space based on HART index */
|
||||
lla tp, _fw_end
|
||||
@@ -519,6 +531,8 @@ _link_start:
|
||||
RISCV_PTR FW_TEXT_START
|
||||
_link_end:
|
||||
RISCV_PTR _fw_reloc_end
|
||||
__fw_rw_offset:
|
||||
RISCV_PTR _fw_rw_start - _fw_start
|
||||
|
||||
.section .entry, "ax", %progbits
|
||||
.align 3
|
||||
@@ -743,6 +757,8 @@ memcmp:
|
||||
.globl _trap_handler
|
||||
.globl _trap_exit
|
||||
_trap_handler:
|
||||
sfence.vma zero, t0
|
||||
|
||||
TRAP_SAVE_AND_SETUP_SP_T0
|
||||
|
||||
TRAP_SAVE_MEPC_MSTATUS 0
|
||||
|
||||
@@ -30,17 +30,41 @@
|
||||
|
||||
/* Beginning of the read-only data sections */
|
||||
|
||||
PROVIDE(_rodata_start = .);
|
||||
|
||||
.rodata :
|
||||
{
|
||||
PROVIDE(_rodata_start = .);
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE(_rodata_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(0x1000); /* Ensure next section is page aligned */
|
||||
|
||||
.dynsym : {
|
||||
PROVIDE(__dyn_sym_start = .);
|
||||
*(.dynsym)
|
||||
PROVIDE(__dyn_sym_end = .);
|
||||
}
|
||||
|
||||
.rela.dyn : {
|
||||
PROVIDE(__rel_dyn_start = .);
|
||||
*(.rela*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE(__rel_dyn_end = .);
|
||||
}
|
||||
|
||||
PROVIDE(_rodata_end = .);
|
||||
|
||||
/* End of the read-only data sections */
|
||||
|
||||
. = ALIGN(0x1000); /* Ensure next section is page aligned */
|
||||
/*
|
||||
* PMP regions must be to be power-of-2. RX/RW will have separate
|
||||
* regions, so ensure that the split is power-of-2.
|
||||
*/
|
||||
. = ALIGN(1 << LOG2CEIL((SIZEOF(.rodata) + SIZEOF(.text)
|
||||
+ SIZEOF(.dynsym) + SIZEOF(.rela.dyn))));
|
||||
|
||||
PROVIDE(_fw_rw_start = .);
|
||||
|
||||
/* Beginning of the read-write data sections */
|
||||
|
||||
@@ -59,19 +83,6 @@
|
||||
PROVIDE(_data_end = .);
|
||||
}
|
||||
|
||||
.dynsym : {
|
||||
PROVIDE(__dyn_sym_start = .);
|
||||
*(.dynsym)
|
||||
PROVIDE(__dyn_sym_end = .);
|
||||
}
|
||||
|
||||
.rela.dyn : {
|
||||
PROVIDE(__rel_dyn_start = .);
|
||||
*(.rela*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE(__rel_dyn_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(0x1000); /* Ensure next section is page aligned */
|
||||
|
||||
.bss :
|
||||
|
||||
@@ -25,30 +25,39 @@
|
||||
#define mb() RISCV_FENCE(iorw,iorw)
|
||||
|
||||
/* Read Memory barrier */
|
||||
#define rmb() RISCV_FENCE(ir,ir)
|
||||
#define rmb() RISCV_FENCE(ir,iorw)
|
||||
|
||||
/* Write Memory barrier */
|
||||
#define wmb() RISCV_FENCE(ow,ow)
|
||||
#define wmb() RISCV_FENCE(iorw,ow)
|
||||
|
||||
/* SMP Read & Write Memory barrier */
|
||||
#define smp_mb() RISCV_FENCE(rw,rw)
|
||||
|
||||
/* SMP Read Memory barrier */
|
||||
#define smp_rmb() RISCV_FENCE(r,r)
|
||||
#define smp_rmb() RISCV_FENCE(r,rw)
|
||||
|
||||
/* SMP Write Memory barrier */
|
||||
#define smp_wmb() RISCV_FENCE(w,w)
|
||||
#define smp_wmb() RISCV_FENCE(rw,w)
|
||||
|
||||
/* CPU relax for busy loop */
|
||||
#define cpu_relax() asm volatile ("" : : : "memory")
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#ifdef CONFIG_PLATFORM_SOPHGO_MANGO
|
||||
#define __smp_store_release(p, v) \
|
||||
do { \
|
||||
RISCV_FENCE(rw, w); \
|
||||
*(p) = (v); \
|
||||
RISCV_FENCE(w, rw); \
|
||||
} while (0)
|
||||
#else
|
||||
#define __smp_store_release(p, v) \
|
||||
do { \
|
||||
RISCV_FENCE(rw, w); \
|
||||
*(p) = (v); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define __smp_load_acquire(p) \
|
||||
({ \
|
||||
|
||||
@@ -223,6 +223,8 @@
|
||||
#define ENVCFG_CBIE_INV _UL(0x3)
|
||||
#define ENVCFG_FIOM _UL(0x1)
|
||||
|
||||
#define MCOUNTEREN_TM (_UL(1) << 1)
|
||||
|
||||
/* ===== User-level CSRs ===== */
|
||||
|
||||
/* User Trap Setup (N-extension) */
|
||||
@@ -736,6 +738,8 @@
|
||||
#define SMSTATEEN0_CS (_ULL(1) << SMSTATEEN0_CS_SHIFT)
|
||||
#define SMSTATEEN0_FCSR_SHIFT 1
|
||||
#define SMSTATEEN0_FCSR (_ULL(1) << SMSTATEEN0_FCSR_SHIFT)
|
||||
#define SMSTATEEN0_CONTEXT_SHIFT 57
|
||||
#define SMSTATEEN0_CONTEXT (_ULL(1) << SMSTATEEN0_CONTEXT_SHIFT)
|
||||
#define SMSTATEEN0_IMSIC_SHIFT 58
|
||||
#define SMSTATEEN0_IMSIC (_ULL(1) << SMSTATEEN0_IMSIC_SHIFT)
|
||||
#define SMSTATEEN0_AIA_SHIFT 59
|
||||
|
||||
61
include/sbi/sbi_byteorder.h
Normal file
61
include/sbi/sbi_byteorder.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Ventana Micro Systems Inc.
|
||||
*/
|
||||
|
||||
#ifndef __SBI_BYTEORDER_H__
|
||||
#define __SBI_BYTEORDER_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define BSWAP16(x) ((((x) & 0x00ff) << 8) | \
|
||||
(((x) & 0xff00) >> 8))
|
||||
#define BSWAP32(x) ((((x) & 0x000000ff) << 24) | \
|
||||
(((x) & 0x0000ff00) << 8) | \
|
||||
(((x) & 0x00ff0000) >> 8) | \
|
||||
(((x) & 0xff000000) >> 24))
|
||||
#define BSWAP64(x) ((((x) & 0x00000000000000ffULL) << 56) | \
|
||||
(((x) & 0x000000000000ff00ULL) << 40) | \
|
||||
(((x) & 0x0000000000ff0000ULL) << 24) | \
|
||||
(((x) & 0x00000000ff000000ULL) << 8) | \
|
||||
(((x) & 0x000000ff00000000ULL) >> 8) | \
|
||||
(((x) & 0x0000ff0000000000ULL) >> 24) | \
|
||||
(((x) & 0x00ff000000000000ULL) >> 40) | \
|
||||
(((x) & 0xff00000000000000ULL) >> 56))
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* CPU(little-endian) */
|
||||
#define cpu_to_be16(x) ((uint16_t)BSWAP16(x))
|
||||
#define cpu_to_be32(x) ((uint32_t)BSWAP32(x))
|
||||
#define cpu_to_be64(x) ((uint64_t)BSWAP64(x))
|
||||
|
||||
#define be16_to_cpu(x) ((uint16_t)BSWAP16(x))
|
||||
#define be32_to_cpu(x) ((uint32_t)BSWAP32(x))
|
||||
#define be64_to_cpu(x) ((uint64_t)BSWAP64(x))
|
||||
|
||||
#define cpu_to_le16(x) ((uint16_t)(x))
|
||||
#define cpu_to_le32(x) ((uint32_t)(x))
|
||||
#define cpu_to_le64(x) ((uint64_t)(x))
|
||||
|
||||
#define le16_to_cpu(x) ((uint16_t)(x))
|
||||
#define le32_to_cpu(x) ((uint32_t)(x))
|
||||
#define le64_to_cpu(x) ((uint64_t)(x))
|
||||
#else /* CPU(big-endian) */
|
||||
#define cpu_to_be16(x) ((uint16_t)(x))
|
||||
#define cpu_to_be32(x) ((uint32_t)(x))
|
||||
#define cpu_to_be64(x) ((uint64_t)(x))
|
||||
|
||||
#define be16_to_cpu(x) ((uint16_t)(x))
|
||||
#define be32_to_cpu(x) ((uint32_t)(x))
|
||||
#define be64_to_cpu(x) ((uint64_t)(x))
|
||||
|
||||
#define cpu_to_le16(x) ((uint16_t)BSWAP16(x))
|
||||
#define cpu_to_le32(x) ((uint32_t)BSWAP32(x))
|
||||
#define cpu_to_le64(x) ((uint64_t)BSWAP64(x))
|
||||
|
||||
#define le16_to_cpu(x) ((uint16_t)BSWAP16(x))
|
||||
#define le32_to_cpu(x) ((uint32_t)BSWAP32(x))
|
||||
#define le64_to_cpu(x) ((uint64_t)BSWAP64(x))
|
||||
#endif
|
||||
|
||||
#endif /* __SBI_BYTEORDER_H__ */
|
||||
@@ -19,6 +19,9 @@ struct sbi_console_device {
|
||||
/** Write a character to the console output */
|
||||
void (*console_putc)(char ch);
|
||||
|
||||
/** Write a character string to the console output */
|
||||
unsigned long (*console_puts)(const char *str, unsigned long len);
|
||||
|
||||
/** Read a character from the console input */
|
||||
int (*console_getc)(void);
|
||||
};
|
||||
@@ -33,8 +36,12 @@ void sbi_putc(char ch);
|
||||
|
||||
void sbi_puts(const char *str);
|
||||
|
||||
unsigned long sbi_nputs(const char *str, unsigned long len);
|
||||
|
||||
void sbi_gets(char *s, int maxwidth, char endchar);
|
||||
|
||||
unsigned long sbi_ngets(char *str, unsigned long len);
|
||||
|
||||
int __printf(2, 3) sbi_sprintf(char *out, const char *format, ...);
|
||||
|
||||
int __printf(3, 4) sbi_snprintf(char *out, u32 out_sz, const char *format, ...);
|
||||
|
||||
@@ -36,11 +36,53 @@ struct sbi_domain_memregion {
|
||||
*/
|
||||
unsigned long base;
|
||||
/** Flags representing memory region attributes */
|
||||
#define SBI_DOMAIN_MEMREGION_READABLE (1UL << 0)
|
||||
#define SBI_DOMAIN_MEMREGION_WRITEABLE (1UL << 1)
|
||||
#define SBI_DOMAIN_MEMREGION_EXECUTABLE (1UL << 2)
|
||||
#define SBI_DOMAIN_MEMREGION_MMODE (1UL << 3)
|
||||
#define SBI_DOMAIN_MEMREGION_ACCESS_MASK (0xfUL)
|
||||
#define SBI_DOMAIN_MEMREGION_M_READABLE (1UL << 0)
|
||||
#define SBI_DOMAIN_MEMREGION_M_WRITABLE (1UL << 1)
|
||||
#define SBI_DOMAIN_MEMREGION_M_EXECUTABLE (1UL << 2)
|
||||
#define SBI_DOMAIN_MEMREGION_SU_READABLE (1UL << 3)
|
||||
#define SBI_DOMAIN_MEMREGION_SU_WRITABLE (1UL << 4)
|
||||
#define SBI_DOMAIN_MEMREGION_SU_EXECUTABLE (1UL << 5)
|
||||
|
||||
/** Bit to control if permissions are enforced on all modes */
|
||||
#define SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS (1UL << 6)
|
||||
|
||||
#define SBI_DOMAIN_MEMREGION_M_RWX \
|
||||
(SBI_DOMAIN_MEMREGION_M_READABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_EXECUTABLE)
|
||||
|
||||
#define SBI_DOMAIN_MEMREGION_SU_RWX \
|
||||
(SBI_DOMAIN_MEMREGION_SU_READABLE | \
|
||||
SBI_DOMAIN_MEMREGION_SU_WRITABLE | \
|
||||
SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
|
||||
|
||||
/* Unrestricted M-mode accesses but enfoced on SU-mode */
|
||||
#define SBI_DOMAIN_MEMREGION_READABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_READABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_RWX)
|
||||
#define SBI_DOMAIN_MEMREGION_WRITEABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_WRITABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_RWX)
|
||||
#define SBI_DOMAIN_MEMREGION_EXECUTABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_EXECUTABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_RWX)
|
||||
|
||||
/* Enforced accesses across all modes */
|
||||
#define SBI_DOMAIN_MEMREGION_ENF_READABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_READABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE)
|
||||
#define SBI_DOMAIN_MEMREGION_ENF_WRITABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_WRITABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE)
|
||||
#define SBI_DOMAIN_MEMREGION_ENF_EXECUTABLE \
|
||||
(SBI_DOMAIN_MEMREGION_SU_EXECUTABLE | \
|
||||
SBI_DOMAIN_MEMREGION_M_EXECUTABLE)
|
||||
|
||||
#define SBI_DOMAIN_MEMREGION_ACCESS_MASK (0x3fUL)
|
||||
#define SBI_DOMAIN_MEMREGION_M_ACCESS_MASK (0x7UL)
|
||||
#define SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK (0x38UL)
|
||||
|
||||
#define SBI_DOMAIN_MEMREGION_SU_ACCESS_SHIFT (3)
|
||||
|
||||
#define SBI_DOMAIN_MEMREGION_MMIO (1UL << 31)
|
||||
unsigned long flags;
|
||||
@@ -78,6 +120,8 @@ struct sbi_domain {
|
||||
unsigned long next_mode;
|
||||
/** Is domain allowed to reset the system */
|
||||
bool system_reset_allowed;
|
||||
/** Is domain allowed to suspend the system */
|
||||
bool system_suspend_allowed;
|
||||
};
|
||||
|
||||
/** The root domain instance */
|
||||
@@ -113,7 +157,7 @@ extern struct sbi_domain *domidx_to_domain_table[];
|
||||
* Check whether given HART is assigned to specified domain
|
||||
* @param dom pointer to domain
|
||||
* @param hartid the HART ID
|
||||
* @return TRUE if HART is assigned to domain otherwise FALSE
|
||||
* @return true if HART is assigned to domain otherwise false
|
||||
*/
|
||||
bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid);
|
||||
|
||||
@@ -148,12 +192,27 @@ void sbi_domain_memregion_init(unsigned long addr,
|
||||
* @param addr the address to be checked
|
||||
* @param mode the privilege mode of access
|
||||
* @param access_flags bitmask of domain access types (enum sbi_domain_access)
|
||||
* @return TRUE if access allowed otherwise FALSE
|
||||
* @return true if access allowed otherwise false
|
||||
*/
|
||||
bool sbi_domain_check_addr(const struct sbi_domain *dom,
|
||||
unsigned long addr, unsigned long mode,
|
||||
unsigned long access_flags);
|
||||
|
||||
/**
|
||||
* Check whether we can access specified address range for given mode and
|
||||
* memory region flags under a domain
|
||||
* @param dom pointer to domain
|
||||
* @param addr the start of the address range to be checked
|
||||
* @param size the size of the address range to be checked
|
||||
* @param mode the privilege mode of access
|
||||
* @param access_flags bitmask of domain access types (enum sbi_domain_access)
|
||||
* @return TRUE if access allowed otherwise FALSE
|
||||
*/
|
||||
bool sbi_domain_check_addr_range(const struct sbi_domain *dom,
|
||||
unsigned long addr, unsigned long size,
|
||||
unsigned long mode,
|
||||
unsigned long access_flags);
|
||||
|
||||
/** Dump domain details on the console */
|
||||
void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix);
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#define SBI_EXT_HSM 0x48534D
|
||||
#define SBI_EXT_SRST 0x53525354
|
||||
#define SBI_EXT_PMU 0x504D55
|
||||
#define SBI_EXT_DBCN 0x4442434E
|
||||
#define SBI_EXT_SUSP 0x53555350
|
||||
|
||||
/* SBI function IDs for BASE extension*/
|
||||
#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
|
||||
@@ -99,6 +101,7 @@
|
||||
#define SBI_EXT_PMU_COUNTER_START 0x3
|
||||
#define SBI_EXT_PMU_COUNTER_STOP 0x4
|
||||
#define SBI_EXT_PMU_COUNTER_FW_READ 0x5
|
||||
#define SBI_EXT_PMU_COUNTER_FW_READ_HI 0x6
|
||||
|
||||
/** General pmu event codes specified in SBI PMU extension */
|
||||
enum sbi_pmu_hw_generic_events_t {
|
||||
@@ -182,6 +185,17 @@ enum sbi_pmu_fw_event_code_id {
|
||||
SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20,
|
||||
SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21,
|
||||
SBI_PMU_FW_MAX,
|
||||
/*
|
||||
* Event codes 22 to 255 are reserved for future use.
|
||||
* Event codes 256 to 65534 are reserved for SBI implementation
|
||||
* specific custom firmware events.
|
||||
*/
|
||||
SBI_PMU_FW_RESERVED_MAX = 0xFFFE,
|
||||
/*
|
||||
* Event code 0xFFFF is used for platform specific firmware
|
||||
* events where the event data contains any event specific information.
|
||||
*/
|
||||
SBI_PMU_FW_PLATFORM = 0xFFFF,
|
||||
};
|
||||
|
||||
/** SBI PMU event idx type */
|
||||
@@ -230,6 +244,18 @@ enum sbi_pmu_ctr_type {
|
||||
/* Flags defined for counter stop function */
|
||||
#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
|
||||
|
||||
/* SBI function IDs for DBCN extension */
|
||||
#define SBI_EXT_DBCN_CONSOLE_WRITE 0x0
|
||||
#define SBI_EXT_DBCN_CONSOLE_READ 0x1
|
||||
#define SBI_EXT_DBCN_CONSOLE_WRITE_BYTE 0x2
|
||||
|
||||
/* SBI function IDs for SUSP extension */
|
||||
#define SBI_EXT_SUSP_SUSPEND 0x0
|
||||
|
||||
#define SBI_SUSP_SLEEP_TYPE_SUSPEND 0x0
|
||||
#define SBI_SUSP_SLEEP_TYPE_LAST SBI_SUSP_SLEEP_TYPE_SUSPEND
|
||||
#define SBI_SUSP_PLATFORM_SLEEP_START 0x80000000
|
||||
|
||||
/* SBI base specification related macros */
|
||||
#define SBI_SPEC_VERSION_MAJOR_OFFSET 24
|
||||
#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
|
||||
|
||||
@@ -21,8 +21,12 @@ struct sbi_hsm_device {
|
||||
int (*hart_start)(u32 hartid, ulong saddr);
|
||||
|
||||
/**
|
||||
* Stop (or power-down) the current hart from running. This call
|
||||
* doesn't expect to return if success.
|
||||
* Stop (or power-down) the current hart from running.
|
||||
*
|
||||
* Return SBI_ENOTSUPP if the hart does not support platform-specific
|
||||
* stop actions.
|
||||
*
|
||||
* For successful stop, the call won't return.
|
||||
*/
|
||||
int (*hart_stop)(void);
|
||||
|
||||
@@ -59,15 +63,21 @@ void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch);
|
||||
|
||||
int sbi_hsm_hart_start(struct sbi_scratch *scratch,
|
||||
const struct sbi_domain *dom,
|
||||
u32 hartid, ulong saddr, ulong smode, ulong priv);
|
||||
u32 hartid, ulong saddr, ulong smode, ulong arg1);
|
||||
int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow);
|
||||
void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch);
|
||||
void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch);
|
||||
void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch,
|
||||
u32 hartid);
|
||||
int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
ulong raddr, ulong rmode, ulong priv);
|
||||
ulong raddr, ulong rmode, ulong arg1);
|
||||
bool sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate,
|
||||
long newstate);
|
||||
int __sbi_hsm_hart_get_state(u32 hartid);
|
||||
int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid);
|
||||
int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom,
|
||||
ulong hbase, ulong *out_hmask);
|
||||
void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid);
|
||||
void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch);
|
||||
void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
|
||||
u32 hartid);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,6 +41,10 @@ struct sbi_ipi_event_ops {
|
||||
* Update callback to save/enqueue data for remote HART
|
||||
* Note: This is an optional callback and it is called just before
|
||||
* triggering IPI to remote HART.
|
||||
* @return 0 success
|
||||
* @return -1 break IPI, done on local hart
|
||||
* @return -2 need retry
|
||||
|
||||
*/
|
||||
int (* update)(struct sbi_scratch *scratch,
|
||||
struct sbi_scratch *remote_scratch,
|
||||
@@ -77,6 +81,8 @@ void sbi_ipi_process(void);
|
||||
|
||||
int sbi_ipi_raw_send(u32 target_hart);
|
||||
|
||||
void sbi_ipi_raw_clear(u32 target_hart);
|
||||
|
||||
const struct sbi_ipi_device *sbi_ipi_get_device(void);
|
||||
|
||||
void sbi_ipi_set_device(const struct sbi_ipi_device *dev);
|
||||
|
||||
@@ -47,7 +47,7 @@ static inline void __sbi_list_add(struct sbi_dlist *new,
|
||||
* Checks if the list is empty or not.
|
||||
* @param head List head
|
||||
*
|
||||
* Retruns TRUE if list is empty, FALSE otherwise.
|
||||
* Returns true if list is empty, false otherwise.
|
||||
*/
|
||||
static inline bool sbi_list_empty(struct sbi_dlist *head)
|
||||
{
|
||||
|
||||
@@ -65,6 +65,12 @@ enum sbi_platform_features {
|
||||
|
||||
/** Platform functions */
|
||||
struct sbi_platform_operations {
|
||||
/* Check if specified HART is allowed to do cold boot */
|
||||
bool (*cold_boot_allowed)(u32 hartid);
|
||||
|
||||
/* Check if platform force emulate time csr, instead of using csr directly */
|
||||
bool (*force_emulate_time_csr)(void);
|
||||
|
||||
/* Platform nascent initialization */
|
||||
int (*nascent_init)(void);
|
||||
|
||||
@@ -123,10 +129,10 @@ struct sbi_platform_operations {
|
||||
/** Exit platform timer for current HART */
|
||||
void (*timer_exit)(void);
|
||||
|
||||
/** platform specific SBI extension implementation probe function */
|
||||
int (*vendor_ext_check)(long extid);
|
||||
/** Check if SBI vendor extension is implemented or not */
|
||||
bool (*vendor_ext_check)(void);
|
||||
/** platform specific SBI extension implementation provider */
|
||||
int (*vendor_ext_provider)(long extid, long funcid,
|
||||
int (*vendor_ext_provider)(long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_value,
|
||||
struct sbi_trap_info *out_trap);
|
||||
@@ -344,16 +350,47 @@ static inline u32 sbi_platform_hart_stack_size(const struct sbi_platform *plat)
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param hartid HART ID
|
||||
*
|
||||
* @return TRUE if HART is invalid and FALSE otherwise
|
||||
* @return true if HART is invalid and false otherwise
|
||||
*/
|
||||
static inline bool sbi_platform_hart_invalid(const struct sbi_platform *plat,
|
||||
u32 hartid)
|
||||
{
|
||||
if (!plat)
|
||||
return TRUE;
|
||||
return true;
|
||||
if (plat->hart_count <= sbi_platform_hart_index(plat, hartid))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether given HART is allowed to do cold boot
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param hartid HART ID
|
||||
*
|
||||
* @return true if HART is allowed to do cold boot and false otherwise
|
||||
*/
|
||||
static inline bool sbi_platform_cold_boot_allowed(
|
||||
const struct sbi_platform *plat,
|
||||
u32 hartid)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->cold_boot_allowed)
|
||||
return sbi_platform_ops(plat)->cold_boot_allowed(hartid);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether given platform should force emulate time CSR
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return true such platform force emulate time CSR, false otherwise
|
||||
*/
|
||||
static inline bool sbi_platform_force_emulate_time_csr(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->force_emulate_time_csr)
|
||||
return sbi_platform_ops(plat)->force_emulate_time_csr();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,7 +414,7 @@ static inline int sbi_platform_nascent_init(const struct sbi_platform *plat)
|
||||
* Early initialization for current HART
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
|
||||
* @param cold_boot whether cold boot (true) or warm_boot (false)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
@@ -393,7 +430,7 @@ static inline int sbi_platform_early_init(const struct sbi_platform *plat,
|
||||
* Final initialization for current HART
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
|
||||
* @param cold_boot whether cold boot (true) or warm_boot (false)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
@@ -538,7 +575,7 @@ static inline int sbi_platform_console_init(const struct sbi_platform *plat)
|
||||
* Initialize the platform interrupt controller for current HART
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
|
||||
* @param cold_boot whether cold boot (true) or warm_boot (false)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
@@ -565,7 +602,7 @@ static inline void sbi_platform_irqchip_exit(const struct sbi_platform *plat)
|
||||
* Initialize the platform IPI support for current HART
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
|
||||
* @param cold_boot whether cold boot (true) or warm_boot (false)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
@@ -592,7 +629,7 @@ static inline void sbi_platform_ipi_exit(const struct sbi_platform *plat)
|
||||
* Initialize the platform timer for current HART
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param cold_boot whether cold boot (TRUE) or warm_boot (FALSE)
|
||||
* @param cold_boot whether cold boot (true) or warm_boot (false)
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
@@ -616,27 +653,25 @@ static inline void sbi_platform_timer_exit(const struct sbi_platform *plat)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a vendor extension is implemented or not.
|
||||
* Check if SBI vendor extension is implemented or not.
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param extid vendor SBI extension id
|
||||
*
|
||||
* @return 0 if extid is not implemented and 1 if implemented
|
||||
* @return false if not implemented and true if implemented
|
||||
*/
|
||||
static inline int sbi_platform_vendor_ext_check(const struct sbi_platform *plat,
|
||||
long extid)
|
||||
static inline bool sbi_platform_vendor_ext_check(
|
||||
const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->vendor_ext_check)
|
||||
return sbi_platform_ops(plat)->vendor_ext_check(extid);
|
||||
return sbi_platform_ops(plat)->vendor_ext_check();
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke platform specific vendor SBI extension implementation.
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
* @param extid vendor SBI extension id
|
||||
* @param funcid SBI function id within the extension id
|
||||
* @param regs pointer to trap registers passed by the caller
|
||||
* @param out_value output value that can be filled by the callee
|
||||
@@ -646,14 +681,14 @@ static inline int sbi_platform_vendor_ext_check(const struct sbi_platform *plat,
|
||||
*/
|
||||
static inline int sbi_platform_vendor_ext_provider(
|
||||
const struct sbi_platform *plat,
|
||||
long extid, long funcid,
|
||||
long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_value,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->vendor_ext_provider) {
|
||||
return sbi_platform_ops(plat)->vendor_ext_provider(extid,
|
||||
funcid, regs,
|
||||
return sbi_platform_ops(plat)->vendor_ext_provider(funcid,
|
||||
regs,
|
||||
out_value,
|
||||
out_trap);
|
||||
}
|
||||
|
||||
@@ -30,37 +30,48 @@ struct sbi_pmu_device {
|
||||
|
||||
/**
|
||||
* Validate event code of custom firmware event
|
||||
* Note: SBI_PMU_FW_MAX <= event_idx_code
|
||||
*/
|
||||
int (*fw_event_validate_code)(uint32_t event_idx_code);
|
||||
int (*fw_event_validate_encoding)(uint32_t hartid, uint64_t event_data);
|
||||
|
||||
/**
|
||||
* Match custom firmware counter with custom firmware event
|
||||
* Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX
|
||||
*/
|
||||
bool (*fw_counter_match_code)(uint32_t counter_index,
|
||||
uint32_t event_idx_code);
|
||||
bool (*fw_counter_match_encoding)(uint32_t hartid,
|
||||
uint32_t counter_index,
|
||||
uint64_t event_data);
|
||||
|
||||
/**
|
||||
* Fetch the max width of this counter in number of bits.
|
||||
*/
|
||||
int (*fw_counter_width)(void);
|
||||
|
||||
/**
|
||||
* Read value of custom firmware counter
|
||||
* Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX
|
||||
*/
|
||||
uint64_t (*fw_counter_read_value)(uint32_t counter_index);
|
||||
uint64_t (*fw_counter_read_value)(uint32_t hartid,
|
||||
uint32_t counter_index);
|
||||
|
||||
/**
|
||||
* Write value to custom firmware counter
|
||||
* Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX
|
||||
*/
|
||||
void (*fw_counter_write_value)(uint32_t hartid, uint32_t counter_index,
|
||||
uint64_t value);
|
||||
|
||||
/**
|
||||
* Start custom firmware counter
|
||||
* Note: SBI_PMU_FW_MAX <= event_idx_code
|
||||
* Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX
|
||||
*/
|
||||
int (*fw_counter_start)(uint32_t counter_index,
|
||||
uint32_t event_idx_code,
|
||||
uint64_t init_val, bool init_val_update);
|
||||
int (*fw_counter_start)(uint32_t hartid, uint32_t counter_index,
|
||||
uint64_t event_data);
|
||||
|
||||
/**
|
||||
* Stop custom firmware counter
|
||||
* Note: 0 <= counter_index < SBI_PMU_FW_CTR_MAX
|
||||
*/
|
||||
int (*fw_counter_stop)(uint32_t counter_index);
|
||||
int (*fw_counter_stop)(uint32_t hartid, uint32_t counter_index);
|
||||
|
||||
/**
|
||||
* Custom enable irq for hardware counter
|
||||
|
||||
@@ -18,28 +18,30 @@
|
||||
#define SBI_SCRATCH_FW_START_OFFSET (0 * __SIZEOF_POINTER__)
|
||||
/** Offset of fw_size member in sbi_scratch */
|
||||
#define SBI_SCRATCH_FW_SIZE_OFFSET (1 * __SIZEOF_POINTER__)
|
||||
/** Offset (in sbi_scratch) of the R/W Offset */
|
||||
#define SBI_SCRATCH_FW_RW_OFFSET (2 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_arg1 member in sbi_scratch */
|
||||
#define SBI_SCRATCH_NEXT_ARG1_OFFSET (2 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_NEXT_ARG1_OFFSET (3 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_addr member in sbi_scratch */
|
||||
#define SBI_SCRATCH_NEXT_ADDR_OFFSET (3 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_NEXT_ADDR_OFFSET (4 * __SIZEOF_POINTER__)
|
||||
/** Offset of next_mode member in sbi_scratch */
|
||||
#define SBI_SCRATCH_NEXT_MODE_OFFSET (4 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_NEXT_MODE_OFFSET (5 * __SIZEOF_POINTER__)
|
||||
/** Offset of warmboot_addr member in sbi_scratch */
|
||||
#define SBI_SCRATCH_WARMBOOT_ADDR_OFFSET (5 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_WARMBOOT_ADDR_OFFSET (6 * __SIZEOF_POINTER__)
|
||||
/** Offset of platform_addr member in sbi_scratch */
|
||||
#define SBI_SCRATCH_PLATFORM_ADDR_OFFSET (6 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_PLATFORM_ADDR_OFFSET (7 * __SIZEOF_POINTER__)
|
||||
/** Offset of hartid_to_scratch member in sbi_scratch */
|
||||
#define SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET (7 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET (8 * __SIZEOF_POINTER__)
|
||||
/** Offset of trap_exit member in sbi_scratch */
|
||||
#define SBI_SCRATCH_TRAP_EXIT_OFFSET (8 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_TRAP_EXIT_OFFSET (9 * __SIZEOF_POINTER__)
|
||||
/** Offset of tmp0 member in sbi_scratch */
|
||||
#define SBI_SCRATCH_TMP0_OFFSET (9 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_TMP0_OFFSET (10 * __SIZEOF_POINTER__)
|
||||
/** Offset of options member in sbi_scratch */
|
||||
#define SBI_SCRATCH_OPTIONS_OFFSET (10 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_OPTIONS_OFFSET (11 * __SIZEOF_POINTER__)
|
||||
/** Offset of extra space in sbi_scratch */
|
||||
#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (11 * __SIZEOF_POINTER__)
|
||||
#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (12 * __SIZEOF_POINTER__)
|
||||
/** Maximum size of sbi_scratch (4KB) */
|
||||
#define SBI_SCRATCH_SIZE (0x1000)
|
||||
#define SBI_SCRATCH_SIZE (0x2000)
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
@@ -53,6 +55,8 @@ struct sbi_scratch {
|
||||
unsigned long fw_start;
|
||||
/** Size (in bytes) of firmware linked to OpenSBI library */
|
||||
unsigned long fw_size;
|
||||
/** Offset (in bytes) of the R/W section */
|
||||
unsigned long fw_rw_offset;
|
||||
/** Arg1 (or 'a1' register) of next booting stage for this HART */
|
||||
unsigned long next_arg1;
|
||||
/** Address of next booting stage for this HART */
|
||||
|
||||
@@ -43,4 +43,31 @@ bool sbi_system_reset_supported(u32 reset_type, u32 reset_reason);
|
||||
|
||||
void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason);
|
||||
|
||||
/** System suspend device */
|
||||
struct sbi_system_suspend_device {
|
||||
/** Name of the system suspend device */
|
||||
char name[32];
|
||||
|
||||
/* Check whether sleep type is supported by the device */
|
||||
int (*system_suspend_check)(u32 sleep_type);
|
||||
|
||||
/**
|
||||
* Suspend the system
|
||||
*
|
||||
* @sleep_type: The sleep type identifier passed to the SBI call.
|
||||
* @mmode_resume_addr:
|
||||
* This is the same as sbi_scratch.warmboot_addr. Some platforms
|
||||
* may not be able to return from system_suspend(), so they will
|
||||
* jump directly to this address instead. Platforms which can
|
||||
* return from system_suspend() may ignore this parameter.
|
||||
*/
|
||||
int (*system_suspend)(u32 sleep_type, unsigned long mmode_resume_addr);
|
||||
};
|
||||
|
||||
const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void);
|
||||
void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev);
|
||||
void sbi_system_suspend_test_enable(void);
|
||||
bool sbi_system_suspend_supported(u32 sleep_type);
|
||||
int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
#define SBI_TLB_FIFO_NUM_ENTRIES 8
|
||||
#define SBI_TLB_FIFO_NUM_ENTRIES 128
|
||||
|
||||
struct sbi_scratch;
|
||||
|
||||
|
||||
@@ -44,7 +44,11 @@ typedef unsigned long long uint64_t;
|
||||
#error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ < 202000L
|
||||
typedef int bool;
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned long uintptr_t;
|
||||
typedef unsigned long size_t;
|
||||
@@ -54,16 +58,19 @@ typedef unsigned long virtual_size_t;
|
||||
typedef unsigned long physical_addr_t;
|
||||
typedef unsigned long physical_size_t;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define true TRUE
|
||||
#define false FALSE
|
||||
typedef uint16_t le16_t;
|
||||
typedef uint16_t be16_t;
|
||||
typedef uint32_t le32_t;
|
||||
typedef uint32_t be32_t;
|
||||
typedef uint64_t le64_t;
|
||||
typedef uint64_t be64_t;
|
||||
|
||||
#define NULL ((void *)0)
|
||||
|
||||
#define __packed __attribute__((packed))
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
#define __aligned(x) __attribute__((aligned(x)))
|
||||
#define __always_inline inline __attribute__((always_inline))
|
||||
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
|
||||
@@ -9,6 +9,29 @@
|
||||
#ifndef __FDT_FIXUP_H__
|
||||
#define __FDT_FIXUP_H__
|
||||
|
||||
struct sbi_cpu_idle_state {
|
||||
const char *name;
|
||||
uint32_t suspend_param;
|
||||
bool local_timer_stop;
|
||||
uint32_t entry_latency_us;
|
||||
uint32_t exit_latency_us;
|
||||
uint32_t min_residency_us;
|
||||
uint32_t wakeup_latency_us;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add CPU idle states to cpu nodes in the DT
|
||||
*
|
||||
* Add information about CPU idle states to the devicetree. This function
|
||||
* assumes that CPU idle states are not already present in the devicetree, and
|
||||
* that all CPU states are equally applicable to all CPUs.
|
||||
*
|
||||
* @param fdt: device tree blob
|
||||
* @param states: array of idle state descriptions, ending with empty element
|
||||
* @return zero on success and -ve on failure
|
||||
*/
|
||||
int fdt_add_cpu_idle_states(void *dtb, const struct sbi_cpu_idle_state *state);
|
||||
|
||||
/**
|
||||
* Fix up the CPU node in the device tree
|
||||
*
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#define __FDT_HELPER_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
|
||||
struct fdt_match {
|
||||
const char *compatible;
|
||||
@@ -109,7 +109,7 @@ int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
|
||||
|
||||
static inline void *fdt_get_address(void)
|
||||
{
|
||||
return sbi_scratch_thishart_arg1_ptr();
|
||||
return (void *)root.next_arg1;
|
||||
}
|
||||
|
||||
#endif /* __FDT_HELPER_H__ */
|
||||
|
||||
21
include/sbi_utils/i2c/dw_i2c.h
Normal file
21
include/sbi_utils/i2c/dw_i2c.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 StarFive Technology Co., Ltd.
|
||||
*
|
||||
* Author: Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#ifndef __DW_I2C_H__
|
||||
#define __DW_I2C_H__
|
||||
|
||||
#include <sbi_utils/i2c/i2c.h>
|
||||
|
||||
int dw_i2c_init(struct i2c_adapter *, int nodeoff);
|
||||
|
||||
struct dw_i2c_adapter {
|
||||
unsigned long addr;
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -38,7 +38,7 @@ enum semihosting_open_mode {
|
||||
|
||||
#ifdef CONFIG_SERIAL_SEMIHOSTING
|
||||
int semihosting_init(void);
|
||||
int semihosting_enabled(void);
|
||||
bool semihosting_enabled(void);
|
||||
#else
|
||||
static inline int semihosting_init(void) { return SBI_ENODEV; }
|
||||
static inline int semihosting_enabled(void) { return 0; }
|
||||
|
||||
59
include/sbi_utils/sys/atcsmu.h
Normal file
59
include/sbi_utils/sys/atcsmu.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Andes Technology Corporation
|
||||
*/
|
||||
|
||||
#ifndef _SYS_ATCSMU_H
|
||||
#define _SYS_ATCSMU_H
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define PCS0_WE_OFFSET 0x90
|
||||
#define PCSm_WE_OFFSET(i) ((i + 3) * 0x20 + PCS0_WE_OFFSET)
|
||||
|
||||
#define PCS0_CTL_OFFSET 0x94
|
||||
#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET)
|
||||
#define PCS_CTL_CMD_SHIFT 0
|
||||
#define PCS_CTL_PARAM_SHIFT 3
|
||||
#define SLEEP_CMD 0x3
|
||||
#define WAKEUP_CMD (0x0 | (1 << PCS_CTL_PARAM_SHIFT))
|
||||
#define LIGHTSLEEP_MODE 0
|
||||
#define DEEPSLEEP_MODE 1
|
||||
#define LIGHT_SLEEP_CMD (SLEEP_CMD | (LIGHTSLEEP_MODE << PCS_CTL_PARAM_SHIFT))
|
||||
#define DEEP_SLEEP_CMD (SLEEP_CMD | (DEEPSLEEP_MODE << PCS_CTL_PARAM_SHIFT))
|
||||
|
||||
#define PCS0_CFG_OFFSET 0x80
|
||||
#define PCSm_CFG_OFFSET(i) ((i + 3) * 0x20 + PCS0_CFG_OFFSET)
|
||||
#define PCS_CFG_LIGHT_SLEEP_SHIFT 2
|
||||
#define PCS_CFG_LIGHT_SLEEP (1 << PCS_CFG_LIGHT_SLEEP_SHIFT)
|
||||
#define PCS_CFG_DEEP_SLEEP_SHIFT 3
|
||||
#define PCS_CFG_DEEP_SLEEP (1 << PCS_CFG_DEEP_SLEEP_SHIFT)
|
||||
|
||||
#define RESET_VEC_LO_OFFSET 0x50
|
||||
#define RESET_VEC_HI_OFFSET 0x60
|
||||
#define RESET_VEC_8CORE_OFFSET 0x1a0
|
||||
#define HARTn_RESET_VEC_LO(n) (RESET_VEC_LO_OFFSET + \
|
||||
((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + \
|
||||
((n) * 0x4))
|
||||
#define HARTn_RESET_VEC_HI(n) (RESET_VEC_HI_OFFSET + \
|
||||
((n) < 4 ? 0 : RESET_VEC_8CORE_OFFSET) + \
|
||||
((n) * 0x4))
|
||||
|
||||
#define PCS_MAX_NR 8
|
||||
#define FLASH_BASE 0x80000000ULL
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
struct smu_data {
|
||||
unsigned long addr;
|
||||
};
|
||||
|
||||
int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid);
|
||||
bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode, u32 hartid);
|
||||
int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid);
|
||||
int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr, u32 hartid);
|
||||
|
||||
#endif /* _SYS_ATCSMU_H */
|
||||
@@ -35,6 +35,7 @@ struct aclint_mtimer_data {
|
||||
u32 hart_count;
|
||||
bool has_64bit_mmio;
|
||||
bool has_shared_mtime;
|
||||
bool use_extern_domain;
|
||||
/* Private details (initialized and used by ACLINT MTIMER library) */
|
||||
struct aclint_mtimer_data *time_delta_reference;
|
||||
unsigned long time_delta_computed;
|
||||
|
||||
21
include/sbi_utils/timer/sg2042_gmt.h
Normal file
21
include/sbi_utils/timer/sg2042_gmt.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 SOPHGO Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Chao Wei <chao.wei@sophgo.com>
|
||||
*/
|
||||
|
||||
#ifndef __SG2042_GMT_H__
|
||||
#define __SG2042_GMT_H__
|
||||
|
||||
int sg2042gmt_cold_timer_init(unsigned long mtimer_base,
|
||||
unsigned long mtimecmp_base,
|
||||
unsigned long mtimecmp_size,
|
||||
unsigned long delcared_freq,
|
||||
unsigned long actual_freq,
|
||||
unsigned long timecmp_freq);
|
||||
int sg2042gmt_warm_timer_init(void);
|
||||
|
||||
#endif /* __SG2042_GMT_H__ */
|
||||
@@ -22,10 +22,18 @@ config SBI_ECALL_SRST
|
||||
bool "System Reset extension"
|
||||
default y
|
||||
|
||||
config SBI_ECALL_SUSP
|
||||
bool "System Suspend extension"
|
||||
default y
|
||||
|
||||
config SBI_ECALL_PMU
|
||||
bool "Performance Monitoring Unit extension"
|
||||
default y
|
||||
|
||||
config SBI_ECALL_DBCN
|
||||
bool "Debug Console extension"
|
||||
default y
|
||||
|
||||
config SBI_ECALL_LEGACY
|
||||
bool "SBI v0.1 legacy extensions"
|
||||
default y
|
||||
|
||||
@@ -34,9 +34,15 @@ libsbi-objs-$(CONFIG_SBI_ECALL_HSM) += sbi_ecall_hsm.o
|
||||
carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_SRST) += ecall_srst
|
||||
libsbi-objs-$(CONFIG_SBI_ECALL_SRST) += sbi_ecall_srst.o
|
||||
|
||||
carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_SUSP) += ecall_susp
|
||||
libsbi-objs-$(CONFIG_SBI_ECALL_SUSP) += sbi_ecall_susp.o
|
||||
|
||||
carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_PMU) += ecall_pmu
|
||||
libsbi-objs-$(CONFIG_SBI_ECALL_PMU) += sbi_ecall_pmu.o
|
||||
|
||||
carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_DBCN) += ecall_dbcn
|
||||
libsbi-objs-$(CONFIG_SBI_ECALL_DBCN) += sbi_ecall_dbcn.o
|
||||
|
||||
carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_LEGACY) += ecall_legacy
|
||||
libsbi-objs-$(CONFIG_SBI_ECALL_LEGACY) += sbi_ecall_legacy.o
|
||||
|
||||
|
||||
@@ -12,17 +12,22 @@
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
|
||||
#define CONSOLE_TBUF_MAX 256
|
||||
|
||||
static const struct sbi_console_device *console_dev = NULL;
|
||||
static char console_tbuf[CONSOLE_TBUF_MAX];
|
||||
static u32 console_tbuf_len;
|
||||
static spinlock_t console_out_lock = SPIN_LOCK_INITIALIZER;
|
||||
|
||||
bool sbi_isprintable(char c)
|
||||
{
|
||||
if (((31 < c) && (c < 127)) || (c == '\f') || (c == '\r') ||
|
||||
(c == '\n') || (c == '\t')) {
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
int sbi_getc(void)
|
||||
@@ -41,16 +46,49 @@ void sbi_putc(char ch)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long nputs(const char *str, unsigned long len)
|
||||
{
|
||||
unsigned long i, ret;
|
||||
|
||||
if (console_dev && console_dev->console_puts) {
|
||||
ret = console_dev->console_puts(str, len);
|
||||
} else {
|
||||
for (i = 0; i < len; i++)
|
||||
sbi_putc(str[i]);
|
||||
ret = len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void nputs_all(const char *str, unsigned long len)
|
||||
{
|
||||
unsigned long p = 0;
|
||||
|
||||
while (p < len)
|
||||
p += nputs(&str[p], len - p);
|
||||
}
|
||||
|
||||
void sbi_puts(const char *str)
|
||||
{
|
||||
unsigned long len = sbi_strlen(str);
|
||||
|
||||
spin_lock(&console_out_lock);
|
||||
while (*str) {
|
||||
sbi_putc(*str);
|
||||
str++;
|
||||
}
|
||||
nputs_all(str, len);
|
||||
spin_unlock(&console_out_lock);
|
||||
}
|
||||
|
||||
unsigned long sbi_nputs(const char *str, unsigned long len)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
spin_lock(&console_out_lock);
|
||||
ret = nputs(str, len);
|
||||
spin_unlock(&console_out_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sbi_gets(char *s, int maxwidth, char endchar)
|
||||
{
|
||||
int ch;
|
||||
@@ -64,6 +102,21 @@ void sbi_gets(char *s, int maxwidth, char endchar)
|
||||
*retval = '\0';
|
||||
}
|
||||
|
||||
unsigned long sbi_ngets(char *str, unsigned long len)
|
||||
{
|
||||
int ch;
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ch = sbi_getc();
|
||||
if (ch < 0)
|
||||
break;
|
||||
str[i] = ch;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#define PAD_RIGHT 1
|
||||
#define PAD_ZERO 2
|
||||
#define PAD_ALTERNATE 4
|
||||
@@ -183,12 +236,30 @@ static int printi(char **out, u32 *out_len, long long i, int b, int sg,
|
||||
|
||||
static int print(char **out, u32 *out_len, const char *format, va_list args)
|
||||
{
|
||||
int width, flags;
|
||||
int pc = 0;
|
||||
char scr[2];
|
||||
int width, flags, pc = 0;
|
||||
char scr[2], *tout;
|
||||
bool use_tbuf = (!out) ? true : false;
|
||||
unsigned long long tmp;
|
||||
|
||||
/*
|
||||
* The console_tbuf is protected by console_out_lock and
|
||||
* print() is always called with console_out_lock held
|
||||
* when out == NULL.
|
||||
*/
|
||||
if (use_tbuf) {
|
||||
console_tbuf_len = CONSOLE_TBUF_MAX;
|
||||
tout = console_tbuf;
|
||||
out = &tout;
|
||||
out_len = &console_tbuf_len;
|
||||
}
|
||||
|
||||
for (; *format != 0; ++format) {
|
||||
if (use_tbuf && !console_tbuf_len) {
|
||||
nputs_all(console_tbuf, CONSOLE_TBUF_MAX);
|
||||
console_tbuf_len = CONSOLE_TBUF_MAX;
|
||||
tout = console_tbuf;
|
||||
}
|
||||
|
||||
if (*format == '%') {
|
||||
++format;
|
||||
width = flags = 0;
|
||||
@@ -314,6 +385,9 @@ literal:
|
||||
}
|
||||
}
|
||||
|
||||
if (use_tbuf && console_tbuf_len < CONSOLE_TBUF_MAX)
|
||||
nputs_all(console_tbuf, CONSOLE_TBUF_MAX - console_tbuf_len);
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
|
||||
/*
|
||||
* We allocate an extra element because sbi_domain_for_each() expects
|
||||
* the array to be null-terminated.
|
||||
*/
|
||||
struct sbi_domain *domidx_to_domain_table[SBI_DOMAIN_MAX_INDEX + 1] = { 0 };
|
||||
struct sbi_domain *hartid_to_domain_table[SBI_HARTMASK_MAX_BITS] = { 0 };
|
||||
struct sbi_domain *domidx_to_domain_table[SBI_DOMAIN_MAX_INDEX] = { 0 };
|
||||
static u32 domain_count = 0;
|
||||
static bool domain_finalized = false;
|
||||
|
||||
@@ -33,7 +37,8 @@ struct sbi_domain root = {
|
||||
.name = "root",
|
||||
.possible_harts = &root_hmask,
|
||||
.regions = root_memregs,
|
||||
.system_reset_allowed = TRUE,
|
||||
.system_reset_allowed = true,
|
||||
.system_suspend_allowed = true,
|
||||
};
|
||||
|
||||
bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid)
|
||||
@@ -41,7 +46,7 @@ bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartid)
|
||||
if (dom)
|
||||
return sbi_hartmask_test_hart(hartid, &dom->assigned_harts);
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong sbi_domain_get_assigned_hartmask(const struct sbi_domain *dom,
|
||||
@@ -105,54 +110,64 @@ bool sbi_domain_check_addr(const struct sbi_domain *dom,
|
||||
unsigned long addr, unsigned long mode,
|
||||
unsigned long access_flags)
|
||||
{
|
||||
bool rmmio, mmio = FALSE;
|
||||
bool rmmio, mmio = false;
|
||||
struct sbi_domain_memregion *reg;
|
||||
unsigned long rstart, rend, rflags, rwx = 0;
|
||||
unsigned long rstart, rend, rflags, rwx = 0, rrwx = 0;
|
||||
|
||||
if (!dom)
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Use M_{R/W/X} bits because the SU-bits are at the
|
||||
* same relative offsets. If the mode is not M, the SU
|
||||
* bits will fall at same offsets after the shift.
|
||||
*/
|
||||
if (access_flags & SBI_DOMAIN_READ)
|
||||
rwx |= SBI_DOMAIN_MEMREGION_READABLE;
|
||||
rwx |= SBI_DOMAIN_MEMREGION_M_READABLE;
|
||||
|
||||
if (access_flags & SBI_DOMAIN_WRITE)
|
||||
rwx |= SBI_DOMAIN_MEMREGION_WRITEABLE;
|
||||
rwx |= SBI_DOMAIN_MEMREGION_M_WRITABLE;
|
||||
|
||||
if (access_flags & SBI_DOMAIN_EXECUTE)
|
||||
rwx |= SBI_DOMAIN_MEMREGION_EXECUTABLE;
|
||||
rwx |= SBI_DOMAIN_MEMREGION_M_EXECUTABLE;
|
||||
|
||||
if (access_flags & SBI_DOMAIN_MMIO)
|
||||
mmio = TRUE;
|
||||
mmio = true;
|
||||
|
||||
sbi_domain_for_each_memregion(dom, reg) {
|
||||
rflags = reg->flags;
|
||||
if (mode == PRV_M && !(rflags & SBI_DOMAIN_MEMREGION_MMODE))
|
||||
continue;
|
||||
rrwx = (mode == PRV_M ?
|
||||
(rflags & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK) :
|
||||
(rflags & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK)
|
||||
>> SBI_DOMAIN_MEMREGION_SU_ACCESS_SHIFT);
|
||||
|
||||
rstart = reg->base;
|
||||
rend = (reg->order < __riscv_xlen) ?
|
||||
rstart + ((1UL << reg->order) - 1) : -1UL;
|
||||
if (rstart <= addr && addr <= rend) {
|
||||
rmmio = (rflags & SBI_DOMAIN_MEMREGION_MMIO) ? TRUE : FALSE;
|
||||
rmmio = (rflags & SBI_DOMAIN_MEMREGION_MMIO) ? true : false;
|
||||
if (mmio != rmmio)
|
||||
return FALSE;
|
||||
return ((rflags & rwx) == rwx) ? TRUE : FALSE;
|
||||
return false;
|
||||
return ((rrwx & rwx) == rwx) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
return (mode == PRV_M) ? TRUE : FALSE;
|
||||
return (mode == PRV_M) ? true : false;
|
||||
}
|
||||
|
||||
/* Check if region complies with constraints */
|
||||
static bool is_region_valid(const struct sbi_domain_memregion *reg)
|
||||
{
|
||||
if (reg->order < 3 || __riscv_xlen < reg->order)
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
if (reg->order == __riscv_xlen && reg->base != 0)
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
if (reg->order < __riscv_xlen && (reg->base & (BIT(reg->order) - 1)))
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Check if regionA is sub-region of regionB */
|
||||
@@ -168,9 +183,9 @@ static bool is_region_subset(const struct sbi_domain_memregion *regA,
|
||||
(regA_start < regB_end) &&
|
||||
(regB_start < regA_end) &&
|
||||
(regA_end <= regB_end))
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if regionA conflicts regionB */
|
||||
@@ -179,9 +194,9 @@ static bool is_region_conflict(const struct sbi_domain_memregion *regA,
|
||||
{
|
||||
if ((is_region_subset(regA, regB) || is_region_subset(regB, regA)) &&
|
||||
regA->flags == regB->flags)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if regionA should be placed before regionB */
|
||||
@@ -189,13 +204,51 @@ static bool is_region_before(const struct sbi_domain_memregion *regA,
|
||||
const struct sbi_domain_memregion *regB)
|
||||
{
|
||||
if (regA->order < regB->order)
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
if ((regA->order == regB->order) &&
|
||||
(regA->base < regB->base))
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct sbi_domain_memregion *find_region(
|
||||
const struct sbi_domain *dom,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned long rstart, rend;
|
||||
struct sbi_domain_memregion *reg;
|
||||
|
||||
sbi_domain_for_each_memregion(dom, reg) {
|
||||
rstart = reg->base;
|
||||
rend = (reg->order < __riscv_xlen) ?
|
||||
rstart + ((1UL << reg->order) - 1) : -1UL;
|
||||
if (rstart <= addr && addr <= rend)
|
||||
return reg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct sbi_domain_memregion *find_next_subset_region(
|
||||
const struct sbi_domain *dom,
|
||||
const struct sbi_domain_memregion *reg,
|
||||
unsigned long addr)
|
||||
{
|
||||
struct sbi_domain_memregion *sreg, *ret = NULL;
|
||||
|
||||
sbi_domain_for_each_memregion(dom, sreg) {
|
||||
if (sreg == reg || (sreg->base <= addr) ||
|
||||
!is_region_subset(sreg, reg))
|
||||
continue;
|
||||
|
||||
if (!ret || (sreg->base < ret->base) ||
|
||||
((sreg->base == ret->base) && (sreg->order < ret->order)))
|
||||
ret = sreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sanitize_domain(const struct sbi_platform *plat,
|
||||
@@ -237,12 +290,12 @@ static int sanitize_domain(const struct sbi_platform *plat,
|
||||
|
||||
/* Count memory regions and check presence of firmware region */
|
||||
count = 0;
|
||||
have_fw_reg = FALSE;
|
||||
have_fw_reg = false;
|
||||
sbi_domain_for_each_memregion(dom, reg) {
|
||||
if (reg->order == root_fw_region.order &&
|
||||
reg->base == root_fw_region.base &&
|
||||
reg->flags == root_fw_region.flags)
|
||||
have_fw_reg = TRUE;
|
||||
have_fw_reg = true;
|
||||
count++;
|
||||
}
|
||||
if (!have_fw_reg) {
|
||||
@@ -285,7 +338,7 @@ static int sanitize_domain(const struct sbi_platform *plat,
|
||||
/*
|
||||
* Check next mode
|
||||
*
|
||||
* We only allow next mode to be S-mode or U-mode.so that we can
|
||||
* We only allow next mode to be S-mode or U-mode, so that we can
|
||||
* protect M-mode context and enforce checks on memory accesses.
|
||||
*/
|
||||
if (dom->next_mode != PRV_S &&
|
||||
@@ -295,7 +348,7 @@ static int sanitize_domain(const struct sbi_platform *plat,
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
/* Check next address and next mode*/
|
||||
/* Check next address and next mode */
|
||||
if (!sbi_domain_check_addr(dom, dom->next_addr, dom->next_mode,
|
||||
SBI_DOMAIN_EXECUTE)) {
|
||||
sbi_printf("%s: %s next booting stage address 0x%lx can't "
|
||||
@@ -306,6 +359,37 @@ static int sanitize_domain(const struct sbi_platform *plat,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sbi_domain_check_addr_range(const struct sbi_domain *dom,
|
||||
unsigned long addr, unsigned long size,
|
||||
unsigned long mode,
|
||||
unsigned long access_flags)
|
||||
{
|
||||
unsigned long max = addr + size;
|
||||
const struct sbi_domain_memregion *reg, *sreg;
|
||||
|
||||
if (!dom)
|
||||
return false;
|
||||
|
||||
while (addr < max) {
|
||||
reg = find_region(dom, addr);
|
||||
if (!reg)
|
||||
return false;
|
||||
|
||||
if (!sbi_domain_check_addr(dom, addr, mode, access_flags))
|
||||
return false;
|
||||
|
||||
sreg = find_next_subset_region(dom, reg, addr);
|
||||
if (sreg)
|
||||
addr = sreg->base;
|
||||
else if (reg->order < __riscv_xlen)
|
||||
addr = reg->base + (1UL << reg->order);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
{
|
||||
u32 i, k;
|
||||
@@ -335,15 +419,25 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
dom->index, i, suffix, rstart, rend);
|
||||
|
||||
k = 0;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE)
|
||||
sbi_printf("%cM", (k++) ? ',' : '(');
|
||||
|
||||
sbi_printf("M: ");
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO)
|
||||
sbi_printf("%cI", (k++) ? ',' : '(');
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_M_READABLE)
|
||||
sbi_printf("%cR", (k++) ? ',' : '(');
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_M_WRITABLE)
|
||||
sbi_printf("%cW", (k++) ? ',' : '(');
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_M_EXECUTABLE)
|
||||
sbi_printf("%cX", (k++) ? ',' : '(');
|
||||
sbi_printf("%s ", (k++) ? ")" : "()");
|
||||
|
||||
k = 0;
|
||||
sbi_printf("S/U: ");
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
|
||||
sbi_printf("%cR", (k++) ? ',' : '(');
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
|
||||
sbi_printf("%cW", (k++) ? ',' : '(');
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
|
||||
sbi_printf("%cX", (k++) ? ',' : '(');
|
||||
sbi_printf("%s\n", (k++) ? ")" : "()");
|
||||
|
||||
@@ -374,6 +468,9 @@ void sbi_domain_dump(const struct sbi_domain *dom, const char *suffix)
|
||||
|
||||
sbi_printf("Domain%d SysReset %s: %s\n",
|
||||
dom->index, suffix, (dom->system_reset_allowed) ? "yes" : "no");
|
||||
|
||||
sbi_printf("Domain%d SysSuspend %s: %s\n",
|
||||
dom->index, suffix, (dom->system_suspend_allowed) ? "yes" : "no");
|
||||
}
|
||||
|
||||
void sbi_domain_dump_all(const char *suffix)
|
||||
@@ -618,11 +715,32 @@ int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid)
|
||||
u32 i;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (scratch->fw_rw_offset == 0 ||
|
||||
(scratch->fw_rw_offset & (scratch->fw_rw_offset - 1)) != 0) {
|
||||
sbi_printf("%s: fw_rw_offset is not a power of 2 (0x%lx)\n",
|
||||
__func__, scratch->fw_rw_offset);
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
if ((scratch->fw_start & (scratch->fw_rw_offset - 1)) != 0) {
|
||||
sbi_printf("%s: fw_start and fw_rw_offset not aligned\n",
|
||||
__func__);
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
/* Root domain firmware memory region */
|
||||
sbi_domain_memregion_init(scratch->fw_start, scratch->fw_size, 0,
|
||||
sbi_domain_memregion_init(scratch->fw_start, scratch->fw_rw_offset,
|
||||
(SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_EXECUTABLE),
|
||||
&root_fw_region);
|
||||
domain_memregion_initfw(&root_memregs[root_memregs_count++]);
|
||||
|
||||
sbi_domain_memregion_init((scratch->fw_start + scratch->fw_rw_offset),
|
||||
(scratch->fw_size - scratch->fw_rw_offset),
|
||||
(SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE),
|
||||
&root_memregs[root_memregs_count++]);
|
||||
|
||||
/* Root domain allow everything memory region */
|
||||
sbi_domain_memregion_init(0, ~0UL,
|
||||
(SBI_DOMAIN_MEMREGION_READABLE |
|
||||
|
||||
@@ -78,7 +78,7 @@ int sbi_ecall_register_extension(struct sbi_ecall_extension *ext)
|
||||
|
||||
void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext)
|
||||
{
|
||||
bool found = FALSE;
|
||||
bool found = false;
|
||||
struct sbi_ecall_extension *t;
|
||||
|
||||
if (!ext)
|
||||
@@ -86,7 +86,7 @@ void sbi_ecall_unregister_extension(struct sbi_ecall_extension *ext)
|
||||
|
||||
sbi_list_for_each_entry(t, &ecall_exts_list, head) {
|
||||
if (t == ext) {
|
||||
found = TRUE;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ int sbi_ecall_handler(struct sbi_trap_regs *regs)
|
||||
trap.epc = regs->mepc;
|
||||
sbi_trap_redirect(regs, &trap);
|
||||
} else {
|
||||
if (ret < SBI_LAST_ERR) {
|
||||
if (ret < SBI_LAST_ERR || SBI_SUCCESS < ret) {
|
||||
sbi_printf("%s: Invalid error %d for ext=0x%lx "
|
||||
"func=0x%lx\n", __func__, ret,
|
||||
extension_id, func_id);
|
||||
|
||||
72
lib/sbi/sbi_ecall_dbcn.c
Normal file
72
lib/sbi/sbi_ecall_dbcn.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <apatel@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_ecall.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
|
||||
static int sbi_ecall_dbcn_handler(unsigned long extid, unsigned long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_val,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
ulong smode = (csr_read(CSR_MSTATUS) & MSTATUS_MPP) >>
|
||||
MSTATUS_MPP_SHIFT;
|
||||
|
||||
switch (funcid) {
|
||||
case SBI_EXT_DBCN_CONSOLE_WRITE:
|
||||
case SBI_EXT_DBCN_CONSOLE_READ:
|
||||
/*
|
||||
* On RV32, the M-mode can only access the first 4GB of
|
||||
* the physical address space because M-mode does not have
|
||||
* MMU to access full 34-bit physical address space.
|
||||
*
|
||||
* Based on above, we simply fail if the upper 32bits of
|
||||
* the physical address (i.e. a2 register) is non-zero on
|
||||
* RV32.
|
||||
*/
|
||||
#if __riscv_xlen == 32
|
||||
if (regs->a2)
|
||||
return SBI_ERR_FAILED;
|
||||
#endif
|
||||
if (!sbi_domain_check_addr_range(sbi_domain_thishart_ptr(),
|
||||
regs->a1, regs->a0, smode,
|
||||
SBI_DOMAIN_READ|SBI_DOMAIN_WRITE))
|
||||
return SBI_ERR_INVALID_PARAM;
|
||||
if (funcid == SBI_EXT_DBCN_CONSOLE_WRITE)
|
||||
*out_val = sbi_nputs((const char *)regs->a1, regs->a0);
|
||||
else
|
||||
*out_val = sbi_ngets((char *)regs->a1, regs->a0);
|
||||
return 0;
|
||||
case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE:
|
||||
sbi_putc(regs->a0);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
static int sbi_ecall_dbcn_probe(unsigned long extid, unsigned long *out_val)
|
||||
{
|
||||
*out_val = sbi_console_get_device() ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sbi_ecall_extension ecall_dbcn = {
|
||||
.extid_start = SBI_EXT_DBCN,
|
||||
.extid_end = SBI_EXT_DBCN,
|
||||
.handle = sbi_ecall_dbcn_handler,
|
||||
.probe = sbi_ecall_dbcn_probe,
|
||||
};
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_version.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
@@ -33,7 +32,7 @@ static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid,
|
||||
regs->a0, regs->a1, smode, regs->a2);
|
||||
break;
|
||||
case SBI_EXT_HSM_HART_STOP:
|
||||
ret = sbi_hsm_hart_stop(scratch, TRUE);
|
||||
ret = sbi_hsm_hart_stop(scratch, true);
|
||||
break;
|
||||
case SBI_EXT_HSM_HART_GET_STATUS:
|
||||
ret = sbi_hsm_hart_get_state(sbi_domain_thishart_ptr(),
|
||||
@@ -45,7 +44,8 @@ static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid,
|
||||
break;
|
||||
default:
|
||||
ret = SBI_ENOTSUPP;
|
||||
};
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
*out_val = ret;
|
||||
ret = 0;
|
||||
|
||||
@@ -54,6 +54,14 @@ static int sbi_ecall_pmu_handler(unsigned long extid, unsigned long funcid,
|
||||
ret = sbi_pmu_ctr_fw_read(regs->a0, &temp);
|
||||
*out_val = temp;
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_FW_READ_HI:
|
||||
#if __riscv_xlen == 32
|
||||
ret = sbi_pmu_ctr_fw_read(regs->a0, &temp);
|
||||
*out_val = temp >> 32;
|
||||
#else
|
||||
*out_val = 0;
|
||||
#endif
|
||||
break;
|
||||
case SBI_EXT_PMU_COUNTER_START:
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
|
||||
48
lib/sbi/sbi_ecall_susp.c
Normal file
48
lib/sbi/sbi_ecall_susp.c
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
#include <sbi/sbi_ecall.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
|
||||
static int sbi_ecall_susp_handler(unsigned long extid, unsigned long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_val,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
int ret = SBI_ENOTSUPP;
|
||||
|
||||
if (funcid == SBI_EXT_SUSP_SUSPEND)
|
||||
ret = sbi_system_suspend(regs->a0, regs->a1, regs->a2);
|
||||
|
||||
if (ret >= 0) {
|
||||
*out_val = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sbi_ecall_susp_probe(unsigned long extid, unsigned long *out_val)
|
||||
{
|
||||
u32 type, count = 0;
|
||||
|
||||
/*
|
||||
* At least one suspend type should be supported by the
|
||||
* platform for the SBI SUSP extension to be usable.
|
||||
*/
|
||||
for (type = 0; type <= SBI_SUSP_SLEEP_TYPE_LAST; type++) {
|
||||
if (sbi_system_suspend_supported(type))
|
||||
count++;
|
||||
}
|
||||
|
||||
*out_val = count ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sbi_ecall_extension ecall_susp = {
|
||||
.extid_start = SBI_EXT_SUSP,
|
||||
.extid_end = SBI_EXT_SUSP,
|
||||
.handle = sbi_ecall_susp_handler,
|
||||
.probe = sbi_ecall_susp_probe,
|
||||
};
|
||||
@@ -13,12 +13,23 @@
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
|
||||
static inline unsigned long sbi_ecall_vendor_id(void)
|
||||
{
|
||||
return SBI_EXT_VENDOR_START +
|
||||
(csr_read(CSR_MVENDORID) &
|
||||
(SBI_EXT_VENDOR_END - SBI_EXT_VENDOR_START));
|
||||
}
|
||||
|
||||
static int sbi_ecall_vendor_probe(unsigned long extid,
|
||||
unsigned long *out_val)
|
||||
{
|
||||
*out_val = sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr(),
|
||||
extid);
|
||||
if (!sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr()) ||
|
||||
extid != sbi_ecall_vendor_id())
|
||||
*out_val = 0;
|
||||
else
|
||||
*out_val = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -27,8 +38,12 @@ static int sbi_ecall_vendor_handler(unsigned long extid, unsigned long funcid,
|
||||
unsigned long *out_val,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
if (!sbi_platform_vendor_ext_check(sbi_platform_thishart_ptr()) ||
|
||||
extid != sbi_ecall_vendor_id())
|
||||
return SBI_ERR_NOT_SUPPORTED;
|
||||
|
||||
return sbi_platform_vendor_ext_provider(sbi_platform_thishart_ptr(),
|
||||
extid, funcid, regs,
|
||||
funcid, regs,
|
||||
out_val, out_trap);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
|
||||
cen = 0;
|
||||
}
|
||||
|
||||
return ((cen >> hpm_num) & 1) ? TRUE : FALSE;
|
||||
return ((cen >> hpm_num) & 1) ? true : false;
|
||||
}
|
||||
|
||||
int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
|
||||
@@ -49,9 +49,9 @@ int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
#if __riscv_xlen == 32
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? true : false;
|
||||
#else
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? true : false;
|
||||
#endif
|
||||
|
||||
switch (csr_num) {
|
||||
@@ -164,9 +164,9 @@ int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs,
|
||||
int ret = 0;
|
||||
ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
#if __riscv_xlen == 32
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
bool virt = (regs->mstatusH & MSTATUSH_MPV) ? true : false;
|
||||
#else
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
bool virt = (regs->mstatus & MSTATUS_MPV) ? true : false;
|
||||
#endif
|
||||
|
||||
switch (csr_num) {
|
||||
|
||||
@@ -26,7 +26,7 @@ void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries,
|
||||
/* Note: must be called with fifo->qlock held */
|
||||
static inline bool __sbi_fifo_is_full(struct sbi_fifo *fifo)
|
||||
{
|
||||
return (fifo->avail == fifo->num_entries) ? TRUE : FALSE;
|
||||
return (fifo->avail == fifo->num_entries) ? true : false;
|
||||
}
|
||||
|
||||
u16 sbi_fifo_avail(struct sbi_fifo *fifo)
|
||||
@@ -75,7 +75,7 @@ static inline void __sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data)
|
||||
/* Note: must be called with fifo->qlock held */
|
||||
static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
{
|
||||
return (fifo->avail == 0) ? TRUE : FALSE;
|
||||
return (fifo->avail == 0) ? true : false;
|
||||
}
|
||||
|
||||
int sbi_fifo_is_empty(struct sbi_fifo *fifo)
|
||||
@@ -105,13 +105,13 @@ static inline void __sbi_fifo_reset(struct sbi_fifo *fifo)
|
||||
bool sbi_fifo_reset(struct sbi_fifo *fifo)
|
||||
{
|
||||
if (!fifo)
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
spin_lock(&fifo->qlock);
|
||||
__sbi_fifo_reset(fifo);
|
||||
spin_unlock(&fifo->qlock);
|
||||
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ static unsigned long hart_features_offset;
|
||||
|
||||
static void mstatus_init(struct sbi_scratch *scratch)
|
||||
{
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
unsigned long menvcfg_val, mstatus_val = 0;
|
||||
int cidx;
|
||||
unsigned int num_mhpm = sbi_hart_mhpm_count(scratch);
|
||||
@@ -62,6 +63,11 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
|
||||
if (sbi_platform_force_emulate_time_csr(plat)) {
|
||||
csr_clear(CSR_MCOUNTEREN, MCOUNTEREN_TM);
|
||||
csr_clear(CSR_SCOUNTEREN, MCOUNTEREN_TM);
|
||||
}
|
||||
|
||||
/* All programmable counters will start running at runtime after S-mode request */
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
@@ -90,6 +96,7 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
mstateen_val |= ((uint64_t)csr_read(CSR_MSTATEEN0H)) << 32;
|
||||
#endif
|
||||
mstateen_val |= SMSTATEEN_STATEN;
|
||||
mstateen_val |= SMSTATEEN0_CONTEXT;
|
||||
mstateen_val |= SMSTATEEN0_HSENVCFG;
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMAIA))
|
||||
@@ -303,15 +310,21 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
|
||||
break;
|
||||
|
||||
pmp_flags = 0;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE)
|
||||
pmp_flags |= PMP_R;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE)
|
||||
pmp_flags |= PMP_W;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)
|
||||
pmp_flags |= PMP_X;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_MMODE)
|
||||
|
||||
/*
|
||||
* If permissions are to be enforced for all modes on this
|
||||
* region, the lock bit should be set.
|
||||
*/
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_ENF_PERMISSIONS)
|
||||
pmp_flags |= PMP_L;
|
||||
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
|
||||
pmp_flags |= PMP_R;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
|
||||
pmp_flags |= PMP_W;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
|
||||
pmp_flags |= PMP_X;
|
||||
|
||||
pmp_addr = reg->base >> PMP_SHIFT;
|
||||
if (pmp_gran_log2 <= reg->order && pmp_addr < pmp_addr_max)
|
||||
pmp_set(pmp_idx++, pmp_flags, reg->base, reg->order);
|
||||
@@ -551,6 +564,7 @@ static int hart_pmu_get_allowed_bits(void)
|
||||
static int hart_detect_features(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_trap_info trap = {0};
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
struct sbi_hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
unsigned long val, oldval;
|
||||
@@ -666,10 +680,12 @@ __mhpm_skip:
|
||||
}
|
||||
|
||||
/* Detect if hart supports time CSR */
|
||||
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_TIME, true);
|
||||
if (!sbi_platform_force_emulate_time_csr(plat)) {
|
||||
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_TIME, true);
|
||||
}
|
||||
|
||||
/* Detect if hart has AIA local interrupt CSRs */
|
||||
csr_read_allowed(CSR_MTOPI, (unsigned long)&trap);
|
||||
|
||||
@@ -26,6 +26,15 @@
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
#define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \
|
||||
({ \
|
||||
long state = atomic_cmpxchg(&(hdata)->state, oldstate, newstate); \
|
||||
if (state != (oldstate)) \
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%lu]\n", \
|
||||
__func__, state); \
|
||||
state == (oldstate); \
|
||||
})
|
||||
|
||||
static const struct sbi_hsm_device *hsm_dev = NULL;
|
||||
static unsigned long hart_data_offset;
|
||||
|
||||
@@ -35,9 +44,18 @@ struct sbi_hsm_data {
|
||||
unsigned long suspend_type;
|
||||
unsigned long saved_mie;
|
||||
unsigned long saved_mip;
|
||||
atomic_t start_ticket;
|
||||
};
|
||||
|
||||
static inline int __sbi_hsm_hart_get_state(u32 hartid)
|
||||
bool sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate,
|
||||
long newstate)
|
||||
{
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
return __sbi_hsm_hart_change_state(hdata, oldstate, newstate);
|
||||
}
|
||||
|
||||
int __sbi_hsm_hart_get_state(u32 hartid)
|
||||
{
|
||||
struct sbi_hsm_data *hdata;
|
||||
struct sbi_scratch *scratch;
|
||||
@@ -58,6 +76,32 @@ int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid)
|
||||
return __sbi_hsm_hart_get_state(hartid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to acquire the ticket for the given target hart to make sure only
|
||||
* one hart prepares the start of the target hart.
|
||||
* Returns true if the ticket has been acquired, false otherwise.
|
||||
*
|
||||
* The function has "acquire" semantics: no memory operations following it
|
||||
* in the current hart can be seen before it by other harts.
|
||||
* atomic_cmpxchg() provides the memory barriers needed for that.
|
||||
*/
|
||||
static bool hsm_start_ticket_acquire(struct sbi_hsm_data *hdata)
|
||||
{
|
||||
return (atomic_cmpxchg(&hdata->start_ticket, 0, 1) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the ticket for the given target hart.
|
||||
*
|
||||
* The function has "release" semantics: no memory operations preceding it
|
||||
* in the current hart can be seen after it by other harts.
|
||||
*/
|
||||
static void hsm_start_ticket_release(struct sbi_hsm_data *hdata)
|
||||
{
|
||||
RISCV_FENCE(rw, w);
|
||||
atomic_write(&hdata->start_ticket, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ulong HART mask for given HART base ID
|
||||
* @param dom the domain to be used for output HART mask
|
||||
@@ -93,16 +137,25 @@ int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid)
|
||||
void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
|
||||
u32 hartid)
|
||||
{
|
||||
u32 oldstate;
|
||||
unsigned long next_arg1;
|
||||
unsigned long next_addr;
|
||||
unsigned long next_mode;
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_START_PENDING,
|
||||
SBI_HSM_STATE_STARTED);
|
||||
if (oldstate != SBI_HSM_STATE_START_PENDING)
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_START_PENDING,
|
||||
SBI_HSM_STATE_STARTED))
|
||||
sbi_hart_hang();
|
||||
|
||||
next_arg1 = scratch->next_arg1;
|
||||
next_addr = scratch->next_addr;
|
||||
next_mode = scratch->next_mode;
|
||||
hsm_start_ticket_release(hdata);
|
||||
|
||||
sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false);
|
||||
}
|
||||
|
||||
static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
|
||||
@@ -116,7 +169,7 @@ static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Set MSIE and MEIE bits to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
|
||||
|
||||
/* Wait for hart_add call*/
|
||||
/* Wait for state transition requested by sbi_hsm_hart_start() */
|
||||
while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) {
|
||||
wfi();
|
||||
};
|
||||
@@ -207,6 +260,7 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
(i == hartid) ?
|
||||
SBI_HSM_STATE_START_PENDING :
|
||||
SBI_HSM_STATE_STOPPED);
|
||||
ATOMIC_INIT(&hdata->start_ticket, 0);
|
||||
}
|
||||
} else {
|
||||
sbi_hsm_hart_wait(scratch, hartid);
|
||||
@@ -217,20 +271,17 @@ int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
|
||||
void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
u32 hstate;
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
|
||||
|
||||
hstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STOP_PENDING,
|
||||
SBI_HSM_STATE_STOPPED);
|
||||
if (hstate != SBI_HSM_STATE_STOP_PENDING)
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STOP_PENDING,
|
||||
SBI_HSM_STATE_STOPPED))
|
||||
goto fail_exit;
|
||||
|
||||
if (hsm_device_has_hart_hotplug()) {
|
||||
hsm_device_hart_stop();
|
||||
/* It should never reach here */
|
||||
goto fail_exit;
|
||||
if (hsm_device_hart_stop() != SBI_ENOTSUPP)
|
||||
goto fail_exit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,12 +299,13 @@ fail_exit:
|
||||
|
||||
int sbi_hsm_hart_start(struct sbi_scratch *scratch,
|
||||
const struct sbi_domain *dom,
|
||||
u32 hartid, ulong saddr, ulong smode, ulong priv)
|
||||
u32 hartid, ulong saddr, ulong smode, ulong arg1)
|
||||
{
|
||||
unsigned long init_count;
|
||||
unsigned int hstate;
|
||||
struct sbi_scratch *rscratch;
|
||||
struct sbi_hsm_data *hdata;
|
||||
int rc;
|
||||
|
||||
/* For now, we only allow start mode to be S-mode or U-mode. */
|
||||
if (smode != PRV_S && smode != PRV_U)
|
||||
@@ -267,39 +319,53 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
|
||||
rscratch = sbi_hartid_to_scratch(hartid);
|
||||
if (!rscratch)
|
||||
return SBI_EINVAL;
|
||||
|
||||
hdata = sbi_scratch_offset_ptr(rscratch, hart_data_offset);
|
||||
if (!hsm_start_ticket_acquire(hdata))
|
||||
return SBI_EINVAL;
|
||||
|
||||
init_count = sbi_init_count(hartid);
|
||||
rscratch->next_arg1 = arg1;
|
||||
rscratch->next_addr = saddr;
|
||||
rscratch->next_mode = smode;
|
||||
|
||||
/*
|
||||
* atomic_cmpxchg() is an implicit barrier. It makes sure that
|
||||
* other harts see reading of init_count and writing to *rscratch
|
||||
* before hdata->state is set to SBI_HSM_STATE_START_PENDING.
|
||||
*/
|
||||
hstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STOPPED,
|
||||
SBI_HSM_STATE_START_PENDING);
|
||||
if (hstate == SBI_HSM_STATE_STARTED)
|
||||
return SBI_EALREADY;
|
||||
if (hstate == SBI_HSM_STATE_STARTED) {
|
||||
rc = SBI_EALREADY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/**
|
||||
* if a hart is already transition to start or stop, another start call
|
||||
* is considered as invalid request.
|
||||
*/
|
||||
if (hstate != SBI_HSM_STATE_STOPPED)
|
||||
return SBI_EINVAL;
|
||||
|
||||
init_count = sbi_init_count(hartid);
|
||||
rscratch->next_arg1 = priv;
|
||||
rscratch->next_addr = saddr;
|
||||
rscratch->next_mode = smode;
|
||||
if (hstate != SBI_HSM_STATE_STOPPED) {
|
||||
rc = SBI_EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (hsm_device_has_hart_hotplug() ||
|
||||
(hsm_device_has_hart_secondary_boot() && !init_count)) {
|
||||
return hsm_device_hart_start(hartid, scratch->warmboot_addr);
|
||||
rc = hsm_device_hart_start(hartid, scratch->warmboot_addr);
|
||||
} else {
|
||||
int rc = sbi_ipi_raw_send(hartid);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = sbi_ipi_raw_send(hartid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (!rc)
|
||||
return 0;
|
||||
err:
|
||||
hsm_start_ticket_release(hdata);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow)
|
||||
{
|
||||
int oldstate;
|
||||
const struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
@@ -307,13 +373,9 @@ int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow)
|
||||
if (!dom)
|
||||
return SBI_EFAIL;
|
||||
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_STOP_PENDING);
|
||||
if (oldstate != SBI_HSM_STATE_STARTED) {
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
|
||||
__func__, oldstate);
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_STOP_PENDING))
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
|
||||
if (exitnow)
|
||||
sbi_exit(scratch);
|
||||
@@ -329,7 +391,7 @@ static int __sbi_hsm_suspend_default(struct sbi_scratch *scratch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch)
|
||||
void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
@@ -358,62 +420,55 @@ static void __sbi_hsm_suspend_non_ret_restore(struct sbi_scratch *scratch)
|
||||
hart_data_offset);
|
||||
|
||||
csr_write(CSR_MIE, hdata->saved_mie);
|
||||
csr_write(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP)));
|
||||
csr_set(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP)));
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch)
|
||||
{
|
||||
int oldstate;
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
|
||||
/* If current HART was SUSPENDED then set RESUME_PENDING state */
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_RESUME_PENDING);
|
||||
if (oldstate != SBI_HSM_STATE_SUSPENDED) {
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
|
||||
__func__, oldstate);
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_RESUME_PENDING))
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
hsm_device_hart_resume();
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch)
|
||||
void __noreturn sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch,
|
||||
u32 hartid)
|
||||
{
|
||||
u32 oldstate;
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
|
||||
/* If current HART was RESUME_PENDING then set STARTED state */
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_RESUME_PENDING,
|
||||
SBI_HSM_STATE_STARTED);
|
||||
if (oldstate != SBI_HSM_STATE_RESUME_PENDING) {
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
|
||||
__func__, oldstate);
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_RESUME_PENDING,
|
||||
SBI_HSM_STATE_STARTED))
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore some of the M-mode CSRs which we are re-configured by
|
||||
* the warm-boot sequence.
|
||||
*/
|
||||
__sbi_hsm_suspend_non_ret_restore(scratch);
|
||||
|
||||
sbi_hart_switch_mode(hartid, scratch->next_arg1,
|
||||
scratch->next_addr,
|
||||
scratch->next_mode, false);
|
||||
}
|
||||
|
||||
int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
ulong raddr, ulong rmode, ulong priv)
|
||||
ulong raddr, ulong rmode, ulong arg1)
|
||||
{
|
||||
int oldstate, ret;
|
||||
int ret;
|
||||
const struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
|
||||
hart_data_offset);
|
||||
|
||||
/* For now, we only allow suspend from S-mode or U-mode. */
|
||||
|
||||
/* Sanity check on domain assigned to current HART */
|
||||
if (!dom)
|
||||
return SBI_EINVAL;
|
||||
return SBI_EFAIL;
|
||||
|
||||
/* Sanity check on suspend type */
|
||||
if (SBI_HSM_SUSPEND_RET_DEFAULT < suspend_type &&
|
||||
@@ -425,27 +480,26 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
|
||||
/* Additional sanity check for non-retentive suspend */
|
||||
if (suspend_type & SBI_HSM_SUSP_NON_RET_BIT) {
|
||||
/*
|
||||
* For now, we only allow non-retentive suspend from
|
||||
* S-mode or U-mode.
|
||||
*/
|
||||
if (rmode != PRV_S && rmode != PRV_U)
|
||||
return SBI_EINVAL;
|
||||
return SBI_EFAIL;
|
||||
if (dom && !sbi_domain_check_addr(dom, raddr, rmode,
|
||||
SBI_DOMAIN_EXECUTE))
|
||||
return SBI_EINVALID_ADDR;
|
||||
}
|
||||
|
||||
/* Save the resume address and resume mode */
|
||||
scratch->next_arg1 = priv;
|
||||
scratch->next_arg1 = arg1;
|
||||
scratch->next_addr = raddr;
|
||||
scratch->next_mode = rmode;
|
||||
|
||||
/* Directly move from STARTED to SUSPENDED state */
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_SUSPENDED);
|
||||
if (oldstate != SBI_HSM_STATE_STARTED) {
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
|
||||
__func__, oldstate);
|
||||
ret = SBI_EDENIED;
|
||||
goto fail_restore_state;
|
||||
}
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_SUSPENDED))
|
||||
return SBI_EFAIL;
|
||||
|
||||
/* Save the suspend type */
|
||||
hdata->suspend_type = suspend_type;
|
||||
@@ -480,18 +534,13 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
jump_warmboot();
|
||||
}
|
||||
|
||||
fail_restore_state:
|
||||
/*
|
||||
* We might have successfully resumed from retentive suspend
|
||||
* or suspend failed. In both cases, we restore state of hart.
|
||||
*/
|
||||
oldstate = atomic_cmpxchg(&hdata->state, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_STARTED);
|
||||
if (oldstate != SBI_HSM_STATE_SUSPENDED) {
|
||||
sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
|
||||
__func__, oldstate);
|
||||
if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_STARTED))
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
|
||||
const struct sbi_timer_device *tdev;
|
||||
const struct sbi_console_device *cdev;
|
||||
const struct sbi_system_reset_device *srdev;
|
||||
const struct sbi_system_suspend_device *susp_dev;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS)
|
||||
@@ -103,11 +104,15 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
|
||||
srdev = sbi_system_reset_get_device(SBI_SRST_RESET_TYPE_SHUTDOWN, 0);
|
||||
sbi_printf("Platform Shutdown Device : %s\n",
|
||||
(srdev) ? srdev->name : "---");
|
||||
susp_dev = sbi_system_suspend_get_device();
|
||||
sbi_printf("Platform Suspend Device : %s\n",
|
||||
(susp_dev) ? susp_dev->name : "---");
|
||||
|
||||
/* Firmware details */
|
||||
sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start);
|
||||
sbi_printf("Firmware Size : %d KB\n",
|
||||
(u32)(scratch->fw_size / 1024));
|
||||
sbi_printf("Firmware RW Offset : 0x%lx\n", scratch->fw_rw_offset);
|
||||
|
||||
/* SBI details */
|
||||
sbi_printf("Runtime SBI Version : %d.%d\n",
|
||||
@@ -255,15 +260,15 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (!init_count_offset)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hsm_init(scratch, hartid, TRUE);
|
||||
rc = sbi_hsm_init(scratch, hartid, true);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_platform_early_init(plat, TRUE);
|
||||
rc = sbi_platform_early_init(plat, true);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hart_init(scratch, TRUE);
|
||||
rc = sbi_hart_init(scratch, true);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -271,32 +276,32 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_pmu_init(scratch, TRUE);
|
||||
rc = sbi_pmu_init(scratch, true);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
sbi_boot_print_banner(scratch);
|
||||
|
||||
rc = sbi_irqchip_init(scratch, TRUE);
|
||||
rc = sbi_irqchip_init(scratch, true);
|
||||
if (rc) {
|
||||
sbi_printf("%s: irqchip init failed (error %d)\n",
|
||||
__func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
rc = sbi_ipi_init(scratch, TRUE);
|
||||
rc = sbi_ipi_init(scratch, true);
|
||||
if (rc) {
|
||||
sbi_printf("%s: ipi init failed (error %d)\n", __func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
rc = sbi_tlb_init(scratch, TRUE);
|
||||
rc = sbi_tlb_init(scratch, true);
|
||||
if (rc) {
|
||||
sbi_printf("%s: tlb init failed (error %d)\n", __func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
rc = sbi_timer_init(scratch, TRUE);
|
||||
rc = sbi_timer_init(scratch, true);
|
||||
if (rc) {
|
||||
sbi_printf("%s: timer init failed (error %d)\n", __func__, rc);
|
||||
sbi_hart_hang();
|
||||
@@ -332,7 +337,7 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
* Note: Platform final initialization should be last so that
|
||||
* it sees correct domain assignment and PMP configuration.
|
||||
*/
|
||||
rc = sbi_platform_final_init(plat, TRUE);
|
||||
rc = sbi_platform_final_init(plat, true);
|
||||
if (rc) {
|
||||
sbi_printf("%s: platform final init failed (error %d)\n",
|
||||
__func__, rc);
|
||||
@@ -350,12 +355,11 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
init_count = sbi_scratch_offset_ptr(scratch, init_count_offset);
|
||||
(*init_count)++;
|
||||
|
||||
sbi_hsm_prepare_next_jump(scratch, hartid);
|
||||
sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr,
|
||||
scratch->next_mode, FALSE);
|
||||
sbi_hsm_hart_start_finish(scratch, hartid);
|
||||
}
|
||||
|
||||
static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
|
||||
static void __noreturn init_warm_startup(struct sbi_scratch *scratch,
|
||||
u32 hartid)
|
||||
{
|
||||
int rc;
|
||||
unsigned long *init_count;
|
||||
@@ -364,35 +368,35 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (!init_count_offset)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hsm_init(scratch, hartid, FALSE);
|
||||
rc = sbi_hsm_init(scratch, hartid, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_platform_early_init(plat, FALSE);
|
||||
rc = sbi_platform_early_init(plat, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_hart_init(scratch, FALSE);
|
||||
rc = sbi_hart_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_pmu_init(scratch, FALSE);
|
||||
rc = sbi_pmu_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_irqchip_init(scratch, FALSE);
|
||||
rc = sbi_irqchip_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_ipi_init(scratch, FALSE);
|
||||
rc = sbi_ipi_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_tlb_init(scratch, FALSE);
|
||||
rc = sbi_tlb_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_timer_init(scratch, FALSE);
|
||||
rc = sbi_timer_init(scratch, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -400,17 +404,18 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_platform_final_init(plat, FALSE);
|
||||
rc = sbi_platform_final_init(plat, false);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
init_count = sbi_scratch_offset_ptr(scratch, init_count_offset);
|
||||
(*init_count)++;
|
||||
|
||||
sbi_hsm_prepare_next_jump(scratch, hartid);
|
||||
sbi_hsm_hart_start_finish(scratch, hartid);
|
||||
}
|
||||
|
||||
static void init_warm_resume(struct sbi_scratch *scratch)
|
||||
static void __noreturn init_warm_resume(struct sbi_scratch *scratch,
|
||||
u32 hartid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
@@ -424,7 +429,7 @@ static void init_warm_resume(struct sbi_scratch *scratch)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
sbi_hsm_hart_resume_finish(scratch);
|
||||
sbi_hsm_hart_resume_finish(scratch, hartid);
|
||||
}
|
||||
|
||||
static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
@@ -437,14 +442,12 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (hstate < 0)
|
||||
sbi_hart_hang();
|
||||
|
||||
if (hstate == SBI_HSM_STATE_SUSPENDED)
|
||||
init_warm_resume(scratch);
|
||||
else
|
||||
if (hstate == SBI_HSM_STATE_SUSPENDED) {
|
||||
init_warm_resume(scratch, hartid);
|
||||
} else {
|
||||
sbi_ipi_raw_clear(hartid);
|
||||
init_warm_startup(scratch, hartid);
|
||||
|
||||
sbi_hart_switch_mode(hartid, scratch->next_arg1,
|
||||
scratch->next_addr,
|
||||
scratch->next_mode, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
|
||||
@@ -463,8 +466,8 @@ static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
|
||||
*/
|
||||
void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
{
|
||||
bool next_mode_supported = FALSE;
|
||||
bool coldboot = FALSE;
|
||||
bool next_mode_supported = false;
|
||||
bool coldboot = false;
|
||||
u32 hartid = current_hartid();
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
@@ -474,15 +477,15 @@ void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
|
||||
switch (scratch->next_mode) {
|
||||
case PRV_M:
|
||||
next_mode_supported = TRUE;
|
||||
next_mode_supported = true;
|
||||
break;
|
||||
case PRV_S:
|
||||
if (misa_extension('S'))
|
||||
next_mode_supported = TRUE;
|
||||
next_mode_supported = true;
|
||||
break;
|
||||
case PRV_U:
|
||||
if (misa_extension('U'))
|
||||
next_mode_supported = TRUE;
|
||||
next_mode_supported = true;
|
||||
break;
|
||||
default:
|
||||
sbi_hart_hang();
|
||||
@@ -498,8 +501,11 @@ void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
* HARTs which satisfy above condition.
|
||||
*/
|
||||
|
||||
if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0)
|
||||
coldboot = TRUE;
|
||||
if (sbi_platform_cold_boot_allowed(plat, hartid)) {
|
||||
if (next_mode_supported &&
|
||||
atomic_xchg(&coldboot_lottery, 1) == 0)
|
||||
coldboot = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do platform specific nascent (very early) initialization so
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_init.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
@@ -31,7 +32,7 @@ static unsigned long ipi_data_off;
|
||||
static const struct sbi_ipi_device *ipi_dev = NULL;
|
||||
static const struct sbi_ipi_event_ops *ipi_ops_array[SBI_IPI_EVENT_MAX];
|
||||
|
||||
static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
|
||||
static int sbi_ipi_update(struct sbi_scratch *scratch, u32 remote_hartid,
|
||||
u32 event, void *data)
|
||||
{
|
||||
int ret;
|
||||
@@ -69,6 +70,18 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
|
||||
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_IPI_SENT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_ipi_sync(struct sbi_scratch *scratch, u32 event)
|
||||
{
|
||||
const struct sbi_ipi_event_ops *ipi_ops;
|
||||
|
||||
if ((SBI_IPI_EVENT_MAX <= event) ||
|
||||
!ipi_ops_array[event])
|
||||
return SBI_EINVAL;
|
||||
ipi_ops = ipi_ops_array[event];
|
||||
|
||||
if (ipi_ops->sync)
|
||||
ipi_ops->sync(scratch);
|
||||
|
||||
@@ -82,8 +95,10 @@ static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
|
||||
*/
|
||||
int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data)
|
||||
{
|
||||
int rc;
|
||||
int rc, done;
|
||||
ulong i, m;
|
||||
struct sbi_hartmask target_mask = {0};
|
||||
struct sbi_hartmask retry_mask = {0};
|
||||
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
@@ -93,23 +108,36 @@ int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data)
|
||||
return rc;
|
||||
m &= hmask;
|
||||
|
||||
/* Send IPIs */
|
||||
for (i = hbase; m; i++, m >>= 1) {
|
||||
if (m & 1UL)
|
||||
sbi_ipi_send(scratch, i, event, data);
|
||||
sbi_hartmask_set_hart(i, &target_mask);
|
||||
}
|
||||
} else {
|
||||
hbase = 0;
|
||||
while (!sbi_hsm_hart_interruptible_mask(dom, hbase, &m)) {
|
||||
/* Send IPIs */
|
||||
for (i = hbase; m; i++, m >>= 1) {
|
||||
if (m & 1UL)
|
||||
sbi_ipi_send(scratch, i, event, data);
|
||||
sbi_hartmask_set_hart(i, &target_mask);
|
||||
}
|
||||
hbase += BITS_PER_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
retry_mask = target_mask;
|
||||
do {
|
||||
done = true;
|
||||
sbi_hartmask_for_each_hart(i, &retry_mask) {
|
||||
rc = sbi_ipi_update(scratch, i, event, data);
|
||||
if (rc == -2)
|
||||
done = false;
|
||||
else
|
||||
sbi_hartmask_clear_hart(i, &retry_mask);
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
/* sync IPIs */
|
||||
sbi_ipi_sync(scratch, event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -163,7 +191,7 @@ void sbi_ipi_clear_smode(void)
|
||||
|
||||
static void sbi_ipi_process_halt(struct sbi_scratch *scratch)
|
||||
{
|
||||
sbi_hsm_hart_stop(scratch, TRUE);
|
||||
sbi_hsm_hart_stop(scratch, true);
|
||||
}
|
||||
|
||||
static struct sbi_ipi_event_ops ipi_halt_ops = {
|
||||
@@ -217,6 +245,12 @@ int sbi_ipi_raw_send(u32 target_hart)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_ipi_raw_clear(u32 target_hart)
|
||||
{
|
||||
if (ipi_dev && ipi_dev->ipi_clear)
|
||||
ipi_dev->ipi_clear(target_hart);
|
||||
}
|
||||
|
||||
const struct sbi_ipi_device *sbi_ipi_get_device(void)
|
||||
{
|
||||
return ipi_dev;
|
||||
|
||||
@@ -65,8 +65,12 @@ static uint32_t active_events[SBI_HARTMASK_MAX_BITS][SBI_PMU_HW_CTR_MAX + SBI_PM
|
||||
#endif
|
||||
static unsigned long fw_counters_started[SBI_HARTMASK_MAX_BITS];
|
||||
|
||||
/* Values of firmwares counters on each HART */
|
||||
static uint64_t fw_counters_value[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_CTR_MAX] = {0};
|
||||
/*
|
||||
* Counter values for SBI firmware events and event codes for platform
|
||||
* firmware events. Both are mutually exclusive and hence can optimally share
|
||||
* the same memory.
|
||||
*/
|
||||
static uint64_t fw_counters_data[SBI_HARTMASK_MAX_BITS][SBI_PMU_FW_CTR_MAX] = {0};
|
||||
|
||||
/* Maximum number of hardware events available */
|
||||
static uint32_t num_hw_events;
|
||||
@@ -85,7 +89,7 @@ static uint32_t total_ctrs;
|
||||
* @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
|
||||
* 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)
|
||||
@@ -93,34 +97,41 @@ static bool pmu_event_range_overlap(struct sbi_pmu_hw_event *evtA,
|
||||
/* 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;
|
||||
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 true;
|
||||
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int pmu_event_validate(unsigned long event_idx)
|
||||
static int pmu_event_validate(unsigned long event_idx, uint64_t edata)
|
||||
{
|
||||
uint32_t event_idx_type = get_cidx_type(event_idx);
|
||||
uint32_t event_idx_code = get_cidx_code(event_idx);
|
||||
uint32_t event_idx_code_max = -1;
|
||||
uint32_t cache_ops_result, cache_ops_id, cache_id;
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
switch(event_idx_type) {
|
||||
case SBI_PMU_EVENT_TYPE_HW:
|
||||
event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
|
||||
break;
|
||||
case SBI_PMU_EVENT_TYPE_FW:
|
||||
if (SBI_PMU_FW_MAX <= event_idx_code &&
|
||||
pmu_dev && pmu_dev->fw_event_validate_code)
|
||||
return pmu_dev->fw_event_validate_code(event_idx_code);
|
||||
if ((event_idx_code >= SBI_PMU_FW_MAX &&
|
||||
event_idx_code <= SBI_PMU_FW_RESERVED_MAX) ||
|
||||
event_idx_code > SBI_PMU_FW_PLATFORM)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (SBI_PMU_FW_PLATFORM == event_idx_code &&
|
||||
pmu_dev && pmu_dev->fw_event_validate_encoding)
|
||||
return pmu_dev->fw_event_validate_encoding(hartid,
|
||||
edata);
|
||||
else
|
||||
event_idx_code_max = SBI_PMU_FW_MAX;
|
||||
break;
|
||||
@@ -183,12 +194,20 @@ int sbi_pmu_ctr_fw_read(uint32_t cidx, uint64_t *cval)
|
||||
if (event_idx_type != SBI_PMU_EVENT_TYPE_FW)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (SBI_PMU_FW_MAX <= event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_read_value)
|
||||
fw_counters_value[hartid][cidx - num_hw_ctrs] =
|
||||
pmu_dev->fw_counter_read_value(cidx - num_hw_ctrs);
|
||||
if ((event_code >= SBI_PMU_FW_MAX &&
|
||||
event_code <= SBI_PMU_FW_RESERVED_MAX) ||
|
||||
event_code > SBI_PMU_FW_PLATFORM)
|
||||
return SBI_EINVAL;
|
||||
|
||||
*cval = fw_counters_value[hartid][cidx - num_hw_ctrs];
|
||||
if (SBI_PMU_FW_PLATFORM == event_code) {
|
||||
if (pmu_dev && pmu_dev->fw_counter_read_value)
|
||||
*cval = pmu_dev->fw_counter_read_value(hartid,
|
||||
cidx -
|
||||
num_hw_ctrs);
|
||||
else
|
||||
*cval = 0;
|
||||
} else
|
||||
*cval = fw_counters_data[hartid][cidx - num_hw_ctrs];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -318,8 +337,11 @@ static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
|
||||
if (cidx >= num_hw_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11)
|
||||
goto skip_inhibit_update;
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11) {
|
||||
if (ival_update)
|
||||
pmu_ctr_write_hw(cidx, ival);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some of the hardware may not support mcountinhibit but perf stat
|
||||
@@ -335,12 +357,12 @@ static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
|
||||
pmu_ctr_enable_irq_hw(cidx);
|
||||
if (pmu_dev && pmu_dev->hw_counter_enable_irq)
|
||||
pmu_dev->hw_counter_enable_irq(cidx);
|
||||
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
|
||||
|
||||
skip_inhibit_update:
|
||||
if (ival_update)
|
||||
pmu_ctr_write_hw(cidx, ival);
|
||||
|
||||
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -357,22 +379,35 @@ int sbi_pmu_irq_bit(void)
|
||||
}
|
||||
|
||||
static int pmu_ctr_start_fw(uint32_t cidx, uint32_t event_code,
|
||||
uint64_t ival, bool ival_update)
|
||||
uint64_t event_data, uint64_t ival,
|
||||
bool ival_update)
|
||||
{
|
||||
int ret;
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
if (SBI_PMU_FW_MAX <= event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_start) {
|
||||
ret = pmu_dev->fw_counter_start(cidx - num_hw_ctrs,
|
||||
event_code,
|
||||
ival, ival_update);
|
||||
if (ret)
|
||||
return ret;
|
||||
if ((event_code >= SBI_PMU_FW_MAX &&
|
||||
event_code <= SBI_PMU_FW_RESERVED_MAX) ||
|
||||
event_code > SBI_PMU_FW_PLATFORM)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (SBI_PMU_FW_PLATFORM == event_code) {
|
||||
if (!pmu_dev ||
|
||||
!pmu_dev->fw_counter_write_value ||
|
||||
!pmu_dev->fw_counter_start) {
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
if (ival_update)
|
||||
pmu_dev->fw_counter_write_value(hartid,
|
||||
cidx - num_hw_ctrs,
|
||||
ival);
|
||||
|
||||
return pmu_dev->fw_counter_start(hartid, cidx - num_hw_ctrs,
|
||||
event_data);
|
||||
} else {
|
||||
if (ival_update)
|
||||
fw_counters_data[hartid][cidx - num_hw_ctrs] = ival;
|
||||
}
|
||||
|
||||
if (ival_update)
|
||||
fw_counters_value[hartid][cidx - num_hw_ctrs] = ival;
|
||||
fw_counters_started[hartid] |= BIT(cidx - num_hw_ctrs);
|
||||
|
||||
return 0;
|
||||
@@ -381,17 +416,19 @@ static int pmu_ctr_start_fw(uint32_t cidx, uint32_t event_code,
|
||||
int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
|
||||
unsigned long flags, uint64_t ival)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
int event_idx_type;
|
||||
uint32_t event_code;
|
||||
int ret = SBI_EINVAL;
|
||||
bool bUpdate = FALSE;
|
||||
bool bUpdate = false;
|
||||
int i, cidx;
|
||||
uint64_t edata;
|
||||
|
||||
if ((cbase + sbi_fls(cmask)) >= total_ctrs)
|
||||
return ret;
|
||||
|
||||
if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
|
||||
bUpdate = TRUE;
|
||||
bUpdate = true;
|
||||
|
||||
for_each_set_bit(i, &cmask, total_ctrs) {
|
||||
cidx = i + cbase;
|
||||
@@ -399,8 +436,13 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
|
||||
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(cidx, event_code, ival, bUpdate);
|
||||
else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW) {
|
||||
edata = (event_code == SBI_PMU_FW_PLATFORM) ?
|
||||
fw_counters_data[hartid][cidx - num_hw_ctrs]
|
||||
: 0x0;
|
||||
ret = pmu_ctr_start_fw(cidx, event_code, edata, ival,
|
||||
bUpdate);
|
||||
}
|
||||
else
|
||||
ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
|
||||
}
|
||||
@@ -432,11 +474,17 @@ static int pmu_ctr_stop_hw(uint32_t cidx)
|
||||
|
||||
static int pmu_ctr_stop_fw(uint32_t cidx, uint32_t event_code)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
int ret;
|
||||
|
||||
if (SBI_PMU_FW_MAX <= event_code &&
|
||||
if ((event_code >= SBI_PMU_FW_MAX &&
|
||||
event_code <= SBI_PMU_FW_RESERVED_MAX) ||
|
||||
event_code > SBI_PMU_FW_PLATFORM)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (SBI_PMU_FW_PLATFORM == event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_stop) {
|
||||
ret = pmu_dev->fw_counter_stop(cidx - num_hw_ctrs);
|
||||
ret = pmu_dev->fw_counter_stop(hartid, cidx - num_hw_ctrs);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -487,6 +535,9 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
|
||||
ret = pmu_ctr_stop_hw(cidx);
|
||||
|
||||
if (flag & SBI_PMU_STOP_FLAG_RESET) {
|
||||
if (pmu_dev && pmu_dev->hw_counter_disable_irq)
|
||||
pmu_dev->hw_counter_disable_irq(cidx);
|
||||
|
||||
active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
|
||||
pmu_reset_hw_mhpmevent(cidx);
|
||||
}
|
||||
@@ -640,20 +691,26 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo
|
||||
* check.
|
||||
*/
|
||||
static int pmu_ctr_find_fw(unsigned long cbase, unsigned long cmask,
|
||||
uint32_t event_code, u32 hartid)
|
||||
uint32_t event_code, u32 hartid, uint64_t edata)
|
||||
{
|
||||
int i, cidx;
|
||||
|
||||
if ((event_code >= SBI_PMU_FW_MAX &&
|
||||
event_code <= SBI_PMU_FW_RESERVED_MAX) ||
|
||||
event_code > SBI_PMU_FW_PLATFORM)
|
||||
return SBI_EINVAL;
|
||||
|
||||
for_each_set_bit(i, &cmask, BITS_PER_LONG) {
|
||||
cidx = i + cbase;
|
||||
if (cidx < num_hw_ctrs || total_ctrs <= cidx)
|
||||
continue;
|
||||
if (active_events[hartid][i] != SBI_PMU_EVENT_IDX_INVALID)
|
||||
continue;
|
||||
if (SBI_PMU_FW_MAX <= event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_match_code) {
|
||||
if (!pmu_dev->fw_counter_match_code(cidx - num_hw_ctrs,
|
||||
event_code))
|
||||
if (SBI_PMU_FW_PLATFORM == event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_match_encoding) {
|
||||
if (!pmu_dev->fw_counter_match_encoding(hartid,
|
||||
cidx - num_hw_ctrs,
|
||||
edata))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -675,7 +732,7 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
event_type = pmu_event_validate(event_idx);
|
||||
event_type = pmu_event_validate(event_idx, event_data);
|
||||
if (event_type < 0)
|
||||
return SBI_EINVAL;
|
||||
event_code = get_cidx_code(event_idx);
|
||||
@@ -693,7 +750,11 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
|
||||
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, event_code, hartid);
|
||||
ctr_idx = pmu_ctr_find_fw(cidx_base, cidx_mask, event_code,
|
||||
hartid, event_data);
|
||||
if (event_code == SBI_PMU_FW_PLATFORM)
|
||||
fw_counters_data[hartid][ctr_idx - num_hw_ctrs] =
|
||||
event_data;
|
||||
} else {
|
||||
ctr_idx = pmu_ctr_find_hw(cidx_base, cidx_mask, flags, event_idx,
|
||||
event_data);
|
||||
@@ -711,14 +772,12 @@ skip_match:
|
||||
pmu_ctr_start_hw(ctr_idx, 0, false);
|
||||
} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
|
||||
if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
|
||||
fw_counters_value[hartid][ctr_idx - num_hw_ctrs] = 0;
|
||||
fw_counters_data[hartid][ctr_idx - num_hw_ctrs] = 0;
|
||||
if (flags & SBI_PMU_CFG_FLAG_AUTO_START) {
|
||||
if (SBI_PMU_FW_MAX <= event_code &&
|
||||
if (SBI_PMU_FW_PLATFORM == event_code &&
|
||||
pmu_dev && pmu_dev->fw_counter_start) {
|
||||
ret = pmu_dev->fw_counter_start(
|
||||
ctr_idx - num_hw_ctrs, event_code,
|
||||
fw_counters_value[hartid][ctr_idx - num_hw_ctrs],
|
||||
true);
|
||||
ret = pmu_dev->fw_counter_start(hartid,
|
||||
ctr_idx - num_hw_ctrs, event_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -743,7 +802,7 @@ int sbi_pmu_ctr_incr_fw(enum sbi_pmu_fw_event_code_id fw_id)
|
||||
for (cidx = num_hw_ctrs; cidx < total_ctrs; cidx++) {
|
||||
if (get_cidx_code(active_events[hartid][cidx]) == fw_id &&
|
||||
(fw_counters_started[hartid] & BIT(cidx - num_hw_ctrs))) {
|
||||
fcounter = &fw_counters_value[hartid][cidx - num_hw_ctrs];
|
||||
fcounter = &fw_counters_data[hartid][cidx - num_hw_ctrs];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -761,6 +820,7 @@ unsigned long sbi_pmu_num_ctr(void)
|
||||
|
||||
int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
|
||||
{
|
||||
int width;
|
||||
union sbi_pmu_ctr_info cinfo = {0};
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
@@ -782,6 +842,11 @@ int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
|
||||
cinfo.type = SBI_PMU_CTR_TYPE_FW;
|
||||
/* Firmware counters are always 64 bits wide */
|
||||
cinfo.width = 63;
|
||||
if (pmu_dev && pmu_dev->fw_counter_width) {
|
||||
width = pmu_dev->fw_counter_width();
|
||||
if (width)
|
||||
cinfo.width = width - 1;
|
||||
}
|
||||
}
|
||||
|
||||
*ctr_info = cinfo.value;
|
||||
@@ -797,7 +862,7 @@ static void pmu_reset_event_map(u32 hartid)
|
||||
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++)
|
||||
fw_counters_value[hartid][j] = 0;
|
||||
fw_counters_data[hartid][j] = 0;
|
||||
fw_counters_started[hartid] = 0;
|
||||
}
|
||||
|
||||
@@ -817,12 +882,17 @@ void sbi_pmu_set_device(const struct sbi_pmu_device *dev)
|
||||
void sbi_pmu_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
|
||||
if (sbi_platform_force_emulate_time_csr(plat))
|
||||
csr_clear(CSR_MCOUNTEREN, MCOUNTEREN_TM);
|
||||
|
||||
pmu_reset_event_map(hartid);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_init.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
|
||||
static SBI_LIST_HEAD(reset_devices_list);
|
||||
|
||||
@@ -79,7 +80,7 @@ void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
|
||||
}
|
||||
|
||||
/* Stop current HART */
|
||||
sbi_hsm_hart_stop(scratch, FALSE);
|
||||
sbi_hsm_hart_stop(scratch, false);
|
||||
|
||||
/* Platform specific reset if domain allowed system reset */
|
||||
if (dom->system_reset_allowed) {
|
||||
@@ -92,3 +93,114 @@ void __noreturn sbi_system_reset(u32 reset_type, u32 reset_reason)
|
||||
/* If platform specific reset did not work then do sbi_exit() */
|
||||
sbi_exit(scratch);
|
||||
}
|
||||
|
||||
static const struct sbi_system_suspend_device *suspend_dev = NULL;
|
||||
|
||||
const struct sbi_system_suspend_device *sbi_system_suspend_get_device(void)
|
||||
{
|
||||
return suspend_dev;
|
||||
}
|
||||
|
||||
void sbi_system_suspend_set_device(struct sbi_system_suspend_device *dev)
|
||||
{
|
||||
if (!dev || suspend_dev)
|
||||
return;
|
||||
|
||||
suspend_dev = dev;
|
||||
}
|
||||
|
||||
static int sbi_system_suspend_test_check(u32 sleep_type)
|
||||
{
|
||||
return sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND;
|
||||
}
|
||||
|
||||
static int sbi_system_suspend_test_suspend(u32 sleep_type,
|
||||
unsigned long mmode_resume_addr)
|
||||
{
|
||||
if (sleep_type != SBI_SUSP_SLEEP_TYPE_SUSPEND)
|
||||
return SBI_EINVAL;
|
||||
|
||||
sbi_timer_mdelay(5000);
|
||||
|
||||
/* Wait for interrupt */
|
||||
wfi();
|
||||
|
||||
return SBI_OK;
|
||||
}
|
||||
|
||||
static struct sbi_system_suspend_device sbi_system_suspend_test = {
|
||||
.name = "system-suspend-test",
|
||||
.system_suspend_check = sbi_system_suspend_test_check,
|
||||
.system_suspend = sbi_system_suspend_test_suspend,
|
||||
};
|
||||
|
||||
void sbi_system_suspend_test_enable(void)
|
||||
{
|
||||
sbi_system_suspend_set_device(&sbi_system_suspend_test);
|
||||
}
|
||||
|
||||
bool sbi_system_suspend_supported(u32 sleep_type)
|
||||
{
|
||||
return suspend_dev && suspend_dev->system_suspend_check &&
|
||||
suspend_dev->system_suspend_check(sleep_type);
|
||||
}
|
||||
|
||||
int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque)
|
||||
{
|
||||
int ret = SBI_ENOTSUPP;
|
||||
const struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
|
||||
unsigned int hartid = current_hartid();
|
||||
unsigned long prev_mode;
|
||||
unsigned long i;
|
||||
|
||||
if (!dom || !dom->system_suspend_allowed)
|
||||
return SBI_EFAIL;
|
||||
|
||||
if (!suspend_dev || !suspend_dev->system_suspend)
|
||||
return SBI_EFAIL;
|
||||
|
||||
if (!sbi_system_suspend_supported(sleep_type))
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
prev_mode = (csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
if (prev_mode != PRV_S && prev_mode != PRV_U)
|
||||
return SBI_EFAIL;
|
||||
|
||||
sbi_hartmask_for_each_hart(i, &dom->assigned_harts) {
|
||||
if (i == hartid)
|
||||
continue;
|
||||
if (__sbi_hsm_hart_get_state(i) != SBI_HSM_STATE_STOPPED)
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
|
||||
if (!sbi_domain_check_addr(dom, resume_addr, prev_mode,
|
||||
SBI_DOMAIN_EXECUTE))
|
||||
return SBI_EINVALID_ADDR;
|
||||
|
||||
if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_STARTED,
|
||||
SBI_HSM_STATE_SUSPENDED))
|
||||
return SBI_EFAIL;
|
||||
|
||||
/* Prepare for resume */
|
||||
scratch->next_mode = prev_mode;
|
||||
scratch->next_addr = resume_addr;
|
||||
scratch->next_arg1 = opaque;
|
||||
|
||||
__sbi_hsm_suspend_non_ret_save(scratch);
|
||||
|
||||
/* Suspend */
|
||||
ret = suspend_dev->system_suspend(sleep_type, scratch->warmboot_addr);
|
||||
if (ret != SBI_OK) {
|
||||
if (!sbi_hsm_hart_change_state(scratch, SBI_HSM_STATE_SUSPENDED,
|
||||
SBI_HSM_STATE_STARTED))
|
||||
sbi_hart_hang();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Resume */
|
||||
jump_warmboot();
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
@@ -211,11 +211,11 @@ static void tlb_pmu_incr_fw_ctr(struct sbi_tlb_info *data)
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_VVMA_ASID_SENT);
|
||||
}
|
||||
|
||||
static void tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
static void tlb_process_helper(struct sbi_tlb_info *tinfo)
|
||||
{
|
||||
u32 rhartid;
|
||||
struct sbi_scratch *rscratch = NULL;
|
||||
unsigned long *rtlb_sync = NULL;
|
||||
atomic_t *rtlb_sync = NULL;
|
||||
|
||||
tinfo->local_fn(tinfo);
|
||||
|
||||
@@ -225,47 +225,40 @@ static void tlb_entry_process(struct sbi_tlb_info *tinfo)
|
||||
continue;
|
||||
|
||||
rtlb_sync = sbi_scratch_offset_ptr(rscratch, tlb_sync_off);
|
||||
while (atomic_raw_xchg_ulong(rtlb_sync, 1)) ;
|
||||
atomic_sub_return(rtlb_sync, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void tlb_process_count(struct sbi_scratch *scratch, int count)
|
||||
static int tlb_process_once(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_tlb_info tinfo;
|
||||
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)) {
|
||||
tlb_entry_process(&tinfo);
|
||||
deq_count++;
|
||||
if (deq_count > count)
|
||||
break;
|
||||
|
||||
if (!sbi_fifo_dequeue(tlb_fifo, &tinfo)) {
|
||||
tlb_process_helper(&tinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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))
|
||||
tlb_entry_process(&tinfo);
|
||||
while (!tlb_process_once(scratch));
|
||||
}
|
||||
|
||||
static void tlb_sync(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long *tlb_sync =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_sync_off);
|
||||
atomic_t *tlb_sync =
|
||||
sbi_scratch_offset_ptr(scratch, tlb_sync_off);
|
||||
|
||||
while (!atomic_raw_xchg_ulong(tlb_sync, 0)) {
|
||||
while (atomic_read(tlb_sync) > 0) {
|
||||
/*
|
||||
* While we are waiting for remote hart to set the sync,
|
||||
* consume fifo requests to avoid deadlock.
|
||||
*/
|
||||
tlb_process_count(scratch, 1);
|
||||
tlb_process_once(scratch);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -343,6 +336,7 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
u32 remote_hartid, void *data)
|
||||
{
|
||||
int ret;
|
||||
atomic_t *tlb_sync;
|
||||
struct sbi_fifo *tlb_fifo_r;
|
||||
struct sbi_tlb_info *tinfo = data;
|
||||
u32 curr_hartid = current_hartid();
|
||||
@@ -369,11 +363,7 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
tlb_fifo_r = sbi_scratch_offset_ptr(remote_scratch, tlb_fifo_off);
|
||||
|
||||
ret = sbi_fifo_inplace_update(tlb_fifo_r, data, tlb_update_cb);
|
||||
if (ret != SBI_FIFO_UNCHANGED) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (sbi_fifo_enqueue(tlb_fifo_r, data) < 0) {
|
||||
if (ret == SBI_FIFO_UNCHANGED && sbi_fifo_enqueue(tlb_fifo_r, data) < 0) {
|
||||
/**
|
||||
* For now, Busy loop until there is space in the fifo.
|
||||
* There may be case where target hart is also
|
||||
@@ -382,11 +372,15 @@ static int tlb_update(struct sbi_scratch *scratch,
|
||||
* TODO: Introduce a wait/wakeup event mechanism to handle
|
||||
* this properly.
|
||||
*/
|
||||
tlb_process_count(scratch, 1);
|
||||
tlb_process_once(scratch);
|
||||
sbi_dprintf("hart%d: hart%d tlb fifo full\n",
|
||||
curr_hartid, remote_hartid);
|
||||
return -2;
|
||||
}
|
||||
|
||||
tlb_sync = sbi_scratch_offset_ptr(scratch, tlb_sync_off);
|
||||
atomic_add_return(tlb_sync, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -413,7 +407,7 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
int ret;
|
||||
void *tlb_mem;
|
||||
unsigned long *tlb_sync;
|
||||
atomic_t *tlb_sync;
|
||||
struct sbi_fifo *tlb_q;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
@@ -455,7 +449,7 @@ int sbi_tlb_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
tlb_q = sbi_scratch_offset_ptr(scratch, tlb_fifo_off);
|
||||
tlb_mem = sbi_scratch_offset_ptr(scratch, tlb_fifo_mem_off);
|
||||
|
||||
*tlb_sync = 0;
|
||||
tlb_sync->counter = 0;
|
||||
|
||||
sbi_fifo_init(tlb_q, tlb_mem,
|
||||
SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE);
|
||||
|
||||
@@ -88,12 +88,12 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
{
|
||||
ulong hstatus, vsstatus, prev_mode;
|
||||
#if __riscv_xlen == 32
|
||||
bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
|
||||
bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? true : false;
|
||||
#else
|
||||
bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
|
||||
bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? true : false;
|
||||
#endif
|
||||
/* By default, we redirect to HS-mode */
|
||||
bool next_virt = FALSE;
|
||||
bool next_virt = false;
|
||||
|
||||
/* Sanity check on previous mode */
|
||||
prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
@@ -106,7 +106,7 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
if (misa_extension('H') && prev_virt) {
|
||||
if ((trap->cause < __riscv_xlen) &&
|
||||
(csr_read(CSR_HEDELEG) & BIT(trap->cause))) {
|
||||
next_virt = TRUE;
|
||||
next_virt = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,18 @@ static int __fdt_parse_region(void *fdt, int domain_offset,
|
||||
u32 *region_count = opaque;
|
||||
struct sbi_domain_memregion *region;
|
||||
|
||||
/*
|
||||
* Non-root domains cannot add a region with only M-mode
|
||||
* access permissions. M-mode regions can only be part of
|
||||
* root domain.
|
||||
*
|
||||
* SU permission bits can't be all zeroes when M-mode permission
|
||||
* bits have at least one bit set.
|
||||
*/
|
||||
if (!(region_access & SBI_DOMAIN_MEMREGION_SU_ACCESS_MASK)
|
||||
&& (region_access & SBI_DOMAIN_MEMREGION_M_ACCESS_MASK))
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Find next region of the domain */
|
||||
if (FDT_DOMAIN_REGION_MAX_COUNT <= *region_count)
|
||||
return SBI_EINVAL;
|
||||
@@ -338,9 +350,9 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
* 2) mmio regions protecting M-mode only mmio devices
|
||||
*/
|
||||
sbi_domain_for_each_memregion(&root, reg) {
|
||||
if ((reg->flags & SBI_DOMAIN_MEMREGION_READABLE) ||
|
||||
(reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE) ||
|
||||
(reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE))
|
||||
if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) ||
|
||||
(reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) ||
|
||||
(reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE))
|
||||
continue;
|
||||
if (FDT_DOMAIN_REGION_MAX_COUNT <= val32)
|
||||
return SBI_EINVAL;
|
||||
@@ -401,9 +413,16 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
/* Read "system-reset-allowed" DT property */
|
||||
if (fdt_get_property(fdt, domain_offset,
|
||||
"system-reset-allowed", NULL))
|
||||
dom->system_reset_allowed = TRUE;
|
||||
dom->system_reset_allowed = true;
|
||||
else
|
||||
dom->system_reset_allowed = FALSE;
|
||||
dom->system_reset_allowed = false;
|
||||
|
||||
/* Read "system-suspend-allowed" DT property */
|
||||
if (fdt_get_property(fdt, domain_offset,
|
||||
"system-suspend-allowed", NULL))
|
||||
dom->system_suspend_allowed = true;
|
||||
else
|
||||
dom->system_suspend_allowed = false;
|
||||
|
||||
/* Find /cpus DT node */
|
||||
cpus_offset = fdt_path_offset(fdt, "/cpus");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* fdt_fixup.c - Flat Device Tree parsing helper routines
|
||||
@@ -14,10 +15,96 @@
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_fixup.h>
|
||||
#include <sbi_utils/fdt/fdt_pmu.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
|
||||
{
|
||||
int cpu_node, cpus_node, err, idle_states_node;
|
||||
uint32_t count, phandle;
|
||||
|
||||
err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 1024);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = fdt_find_max_phandle(fdt, &phandle);
|
||||
phandle++;
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
cpus_node = fdt_path_offset(fdt, "/cpus");
|
||||
if (cpus_node < 0)
|
||||
return cpus_node;
|
||||
|
||||
/* Do nothing if the idle-states node already exists. */
|
||||
idle_states_node = fdt_subnode_offset(fdt, cpus_node, "idle-states");
|
||||
if (idle_states_node >= 0)
|
||||
return 0;
|
||||
|
||||
/* Create the idle-states node and its child nodes. */
|
||||
idle_states_node = fdt_add_subnode(fdt, cpus_node, "idle-states");
|
||||
if (idle_states_node < 0)
|
||||
return idle_states_node;
|
||||
|
||||
for (count = 0; state->name; count++, phandle++, state++) {
|
||||
int idle_state_node;
|
||||
|
||||
idle_state_node = fdt_add_subnode(fdt, idle_states_node,
|
||||
state->name);
|
||||
if (idle_state_node < 0)
|
||||
return idle_state_node;
|
||||
|
||||
fdt_setprop_string(fdt, idle_state_node, "compatible",
|
||||
"riscv,idle-state");
|
||||
fdt_setprop_u32(fdt, idle_state_node,
|
||||
"riscv,sbi-suspend-param",
|
||||
state->suspend_param);
|
||||
if (state->local_timer_stop)
|
||||
fdt_setprop_empty(fdt, idle_state_node,
|
||||
"local-timer-stop");
|
||||
fdt_setprop_u32(fdt, idle_state_node, "entry-latency-us",
|
||||
state->entry_latency_us);
|
||||
fdt_setprop_u32(fdt, idle_state_node, "exit-latency-us",
|
||||
state->exit_latency_us);
|
||||
fdt_setprop_u32(fdt, idle_state_node, "min-residency-us",
|
||||
state->min_residency_us);
|
||||
if (state->wakeup_latency_us)
|
||||
fdt_setprop_u32(fdt, idle_state_node,
|
||||
"wakeup-latency-us",
|
||||
state->wakeup_latency_us);
|
||||
fdt_setprop_u32(fdt, idle_state_node, "phandle", phandle);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
/* Link each cpu node to the idle state nodes. */
|
||||
fdt_for_each_subnode(cpu_node, fdt, cpus_node) {
|
||||
const char *device_type;
|
||||
fdt32_t *value;
|
||||
|
||||
/* Only process child nodes with device_type = "cpu". */
|
||||
device_type = fdt_getprop(fdt, cpu_node, "device_type", NULL);
|
||||
if (!device_type || strcmp(device_type, "cpu"))
|
||||
continue;
|
||||
|
||||
/* Allocate space for the list of phandles. */
|
||||
err = fdt_setprop_placeholder(fdt, cpu_node, "cpu-idle-states",
|
||||
count * sizeof(phandle),
|
||||
(void **)&value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Fill in the phandles of the idle state nodes. */
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
value[i] = cpu_to_fdt32(phandle - count + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fdt_cpu_fixup(void *fdt)
|
||||
{
|
||||
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
@@ -200,8 +287,10 @@ int fdt_reserved_memory_fixup(void *fdt)
|
||||
struct sbi_domain_memregion *reg;
|
||||
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
unsigned long filtered_base[PMP_COUNT] = { 0 };
|
||||
unsigned char filtered_order[PMP_COUNT] = { 0 };
|
||||
unsigned long addr, size;
|
||||
int err, parent, i;
|
||||
int err, parent, i, j;
|
||||
int na = fdt_address_cells(fdt, 0);
|
||||
int ns = fdt_size_cells(fdt, 0);
|
||||
|
||||
@@ -259,18 +348,43 @@ int fdt_reserved_memory_fixup(void *fdt)
|
||||
/* Ignore MMIO or READABLE or WRITABLE or EXECUTABLE regions */
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_MMIO)
|
||||
continue;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_READABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE)
|
||||
continue;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_WRITEABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE)
|
||||
continue;
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_EXECUTABLE)
|
||||
if (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)
|
||||
continue;
|
||||
|
||||
if (i > PMP_COUNT) {
|
||||
sbi_printf("%s: Too many memory regions to fixup.\n",
|
||||
__func__);
|
||||
return SBI_ENOSPC;
|
||||
}
|
||||
|
||||
bool overlap = false;
|
||||
addr = reg->base;
|
||||
size = 1UL << reg->order;
|
||||
fdt_resv_memory_update_node(fdt, addr, size, i, parent,
|
||||
(sbi_hart_pmp_count(scratch)) ? false : true);
|
||||
i++;
|
||||
for (j = 0; j < i; j++) {
|
||||
if (addr == filtered_base[j]
|
||||
&& filtered_order[j] < reg->order) {
|
||||
overlap = true;
|
||||
filtered_order[j] = reg->order;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!overlap) {
|
||||
filtered_base[i] = reg->base;
|
||||
filtered_order[i] = reg->order;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
addr = filtered_base[j];
|
||||
size = 1UL << filtered_order[j];
|
||||
fdt_resv_memory_update_node(fdt, addr, size, j, parent,
|
||||
(sbi_hart_pmp_count(scratch))
|
||||
? false : true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
#define FDT_PMU_HW_EVENT_MAX (SBI_PMU_HW_EVENT_MAX * 2)
|
||||
|
||||
@@ -14,6 +14,13 @@ config FDT_GPIO_SIFIVE
|
||||
bool "SiFive GPIO FDT driver"
|
||||
default n
|
||||
|
||||
config FDT_GPIO_SOPHGO
|
||||
bool "Sophgo GPIO FDT driver"
|
||||
default n
|
||||
|
||||
config FDT_GPIO_STARFIVE
|
||||
bool "StarFive GPIO FDT driver"
|
||||
default n
|
||||
endif
|
||||
|
||||
config GPIO
|
||||
|
||||
157
lib/utils/gpio/fdt_gpio_sophgo.c
Normal file
157
lib/utils/gpio/fdt_gpio_sophgo.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Sophgo
|
||||
*
|
||||
* Author:
|
||||
* Chunzhi Lin <chunzhi.lin@sophgo.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
|
||||
#define SOPHGO_GPIO_CHIP_MAX 3
|
||||
|
||||
#define SOPHGO_GPIO_PINS_MIN 0
|
||||
#define SOPHGO_GPIO_PINS_MAX 31
|
||||
#define SOPHGO_GPIO_PINS_DEF 16
|
||||
|
||||
#define SOPHGO_GPIO_SWPORTA_DR_OFFSET 0x00
|
||||
#define SOPHGO_GPIO_SWPORTA_DDR_OFFSET 0x04
|
||||
#define SOPHGO_GPIO_SWPORTA_CTL_OFFSET 0x08
|
||||
|
||||
#define SOPHGO_GPIO_BIT(offset) (1U << (offset))
|
||||
#define SOPHGO_GPIO_STARTUP_FLAG SOPHGO_GPIO_BIT(16)
|
||||
|
||||
struct sophgo_gpio_chip {
|
||||
unsigned long addr;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
static unsigned int sophgo_gpio_chip_count;
|
||||
static struct sophgo_gpio_chip sophgo_gpio_chip_array[SOPHGO_GPIO_CHIP_MAX];
|
||||
|
||||
static int sophgo_gpio_direction_output(struct gpio_pin *gp, int value)
|
||||
{
|
||||
unsigned int v;
|
||||
struct sophgo_gpio_chip *chip =
|
||||
container_of(gp->chip, struct sophgo_gpio_chip, chip);
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DDR_OFFSET));
|
||||
v |= SOPHGO_GPIO_BIT(gp->offset);
|
||||
writel(v, (volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DDR_OFFSET));
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
if (!value)
|
||||
v &= ~SOPHGO_GPIO_BIT(gp->offset);
|
||||
else
|
||||
v |= SOPHGO_GPIO_BIT(gp->offset);
|
||||
writel(v, (volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sophgo_gpio_set(struct gpio_pin *gp, int value)
|
||||
{
|
||||
unsigned int v;
|
||||
struct sophgo_gpio_chip *chip =
|
||||
container_of(gp->chip, struct sophgo_gpio_chip, chip);
|
||||
|
||||
v = readl((volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
|
||||
if (!value)
|
||||
v &= ~SOPHGO_GPIO_BIT(gp->offset);
|
||||
else
|
||||
v |= SOPHGO_GPIO_BIT(gp->offset);
|
||||
|
||||
writel(v, (volatile void *)(chip->addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int sophgo_gpio_addr_get(void *fdt, int nodeoff, unsigned long *addr)
|
||||
{
|
||||
int parent, len;
|
||||
unsigned long addr_high, addr_low;
|
||||
const fdt32_t *prop_addr;
|
||||
|
||||
parent = fdt_parent_offset(fdt, nodeoff);
|
||||
if (parent < 0)
|
||||
return parent;
|
||||
|
||||
prop_addr = fdt_getprop(fdt, parent, "reg", &len);
|
||||
if (!prop_addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
addr_high = fdt32_to_cpu(*prop_addr++);
|
||||
addr_low = fdt32_to_cpu(*prop_addr);
|
||||
|
||||
*addr = addr_high << 32 | addr_low;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sophgo_system_normal_startup_flag(unsigned long addr)
|
||||
{
|
||||
unsigned int v;
|
||||
|
||||
v = readl((volatile void *)(addr + SOPHGO_GPIO_SWPORTA_DDR_OFFSET));
|
||||
v |= SOPHGO_GPIO_STARTUP_FLAG;
|
||||
writel(v, (volatile void *)(addr + SOPHGO_GPIO_SWPORTA_DDR_OFFSET));
|
||||
|
||||
v = readl((volatile void *)(addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
v |= SOPHGO_GPIO_STARTUP_FLAG;
|
||||
writel(v, (volatile void *)(addr + SOPHGO_GPIO_SWPORTA_DR_OFFSET));
|
||||
}
|
||||
|
||||
extern struct fdt_gpio fdt_gpio_sophgo;
|
||||
|
||||
static int sophgo_gpio_init(void *fdt, int nodeoff, u32 phandle,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct sophgo_gpio_chip *chip;
|
||||
unsigned long addr;
|
||||
|
||||
if (SOPHGO_GPIO_CHIP_MAX <= sophgo_gpio_chip_count) {
|
||||
rc = SBI_ENOSPC;
|
||||
return rc;
|
||||
}
|
||||
|
||||
chip = &sophgo_gpio_chip_array[sophgo_gpio_chip_count];
|
||||
|
||||
rc = sophgo_gpio_addr_get(fdt, nodeoff, &addr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
chip->addr = addr;
|
||||
chip->chip.driver = &fdt_gpio_sophgo;
|
||||
chip->chip.id = phandle;
|
||||
chip->chip.ngpio = SOPHGO_GPIO_PINS_MAX;
|
||||
chip->chip.direction_output = sophgo_gpio_direction_output;
|
||||
chip->chip.set = sophgo_gpio_set;
|
||||
rc = gpio_chip_add(&chip->chip);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sophgo_gpio_chip_count++;
|
||||
sophgo_system_normal_startup_flag(addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match sophgo_gpio_match[] = {
|
||||
{ .compatible = "sophgo,gpio0" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_gpio fdt_gpio_sophgo = {
|
||||
.match_table = sophgo_gpio_match,
|
||||
.xlate = fdt_gpio_simple_xlate,
|
||||
.init = sophgo_gpio_init,
|
||||
};
|
||||
117
lib/utils/gpio/fdt_gpio_starfive.c
Normal file
117
lib/utils/gpio/fdt_gpio_starfive.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Starfive
|
||||
*
|
||||
* Authors:
|
||||
* Minda.chen <Minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
|
||||
#define STARFIVE_GPIO_CHIP_MAX 2
|
||||
#define STARFIVE_GPIO_PINS_DEF 64
|
||||
#define STARFIVE_GPIO_OUTVAL 0x40
|
||||
#define STARFIVE_GPIO_MASK 0xff
|
||||
#define STARFIVE_GPIO_REG_SHIFT_MASK 0x3
|
||||
#define STARFIVE_GPIO_SHIFT_BITS 0x3
|
||||
|
||||
struct starfive_gpio_chip {
|
||||
unsigned long addr;
|
||||
struct gpio_chip chip;
|
||||
};
|
||||
|
||||
static unsigned int starfive_gpio_chip_count;
|
||||
static struct starfive_gpio_chip starfive_gpio_chip_array[STARFIVE_GPIO_CHIP_MAX];
|
||||
|
||||
static int starfive_gpio_direction_output(struct gpio_pin *gp, int value)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long reg_addr;
|
||||
u32 bit_mask, shift_bits;
|
||||
struct starfive_gpio_chip *chip =
|
||||
container_of(gp->chip, struct starfive_gpio_chip, chip);
|
||||
|
||||
/* set out en*/
|
||||
reg_addr = chip->addr + gp->offset;
|
||||
reg_addr &= ~(STARFIVE_GPIO_REG_SHIFT_MASK);
|
||||
|
||||
val = readl((void *)(reg_addr));
|
||||
shift_bits = (gp->offset & STARFIVE_GPIO_REG_SHIFT_MASK)
|
||||
<< STARFIVE_GPIO_SHIFT_BITS;
|
||||
bit_mask = STARFIVE_GPIO_MASK << shift_bits;
|
||||
|
||||
val = readl((void *)reg_addr);
|
||||
val &= ~bit_mask;
|
||||
writel(val, (void *)reg_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void starfive_gpio_set(struct gpio_pin *gp, int value)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long reg_addr;
|
||||
u32 bit_mask, shift_bits;
|
||||
struct starfive_gpio_chip *chip =
|
||||
container_of(gp->chip, struct starfive_gpio_chip, chip);
|
||||
|
||||
reg_addr = chip->addr + gp->offset;
|
||||
reg_addr &= ~(STARFIVE_GPIO_REG_SHIFT_MASK);
|
||||
|
||||
shift_bits = (gp->offset & STARFIVE_GPIO_REG_SHIFT_MASK)
|
||||
<< STARFIVE_GPIO_SHIFT_BITS;
|
||||
bit_mask = STARFIVE_GPIO_MASK << shift_bits;
|
||||
/* set output value */
|
||||
val = readl((void *)(reg_addr + STARFIVE_GPIO_OUTVAL));
|
||||
val &= ~bit_mask;
|
||||
val |= value << shift_bits;
|
||||
writel(val, (void *)(reg_addr + STARFIVE_GPIO_OUTVAL));
|
||||
}
|
||||
|
||||
extern struct fdt_gpio fdt_gpio_starfive;
|
||||
|
||||
static int starfive_gpio_init(void *fdt, int nodeoff, u32 phandle,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct starfive_gpio_chip *chip;
|
||||
u64 addr;
|
||||
|
||||
if (starfive_gpio_chip_count >= STARFIVE_GPIO_CHIP_MAX)
|
||||
return SBI_ENOSPC;
|
||||
chip = &starfive_gpio_chip_array[starfive_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_starfive;
|
||||
chip->chip.id = phandle;
|
||||
chip->chip.ngpio = STARFIVE_GPIO_PINS_DEF;
|
||||
chip->chip.direction_output = starfive_gpio_direction_output;
|
||||
chip->chip.set = starfive_gpio_set;
|
||||
rc = gpio_chip_add(&chip->chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
starfive_gpio_chip_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match starfive_gpio_match[] = {
|
||||
{ .compatible = "starfive,jh7110-sys-pinctrl" },
|
||||
{ .compatible = "starfive,iomux-pinctrl" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_gpio fdt_gpio_starfive = {
|
||||
.match_table = starfive_gpio_match,
|
||||
.xlate = fdt_gpio_simple_xlate,
|
||||
.init = starfive_gpio_init,
|
||||
};
|
||||
@@ -13,4 +13,10 @@ libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o
|
||||
carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive
|
||||
libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o
|
||||
|
||||
carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SOPHGO) += fdt_gpio_sophgo
|
||||
libsbiutils-objs-$(CONFIG_FDT_GPIO_SOPHGO) += gpio/fdt_gpio_sophgo.o
|
||||
|
||||
carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_STARFIVE) += fdt_gpio_starfive
|
||||
libsbiutils-objs-$(CONFIG_FDT_GPIO_STARFIVE) += gpio/fdt_gpio_starfive.o
|
||||
|
||||
libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o
|
||||
|
||||
@@ -14,8 +14,16 @@ config FDT_I2C_SIFIVE
|
||||
bool "SiFive I2C FDT driver"
|
||||
default n
|
||||
|
||||
config FDT_I2C_DW
|
||||
bool "Synopsys Designware I2C FDT driver"
|
||||
select I2C_DW
|
||||
default n
|
||||
endif
|
||||
|
||||
config I2C_DW
|
||||
bool "Synopsys Designware I2C support"
|
||||
default n
|
||||
|
||||
config I2C
|
||||
bool "I2C support"
|
||||
default n
|
||||
|
||||
190
lib/utils/i2c/dw_i2c.c
Normal file
190
lib/utils/i2c/dw_i2c.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 starfivetech.com
|
||||
*
|
||||
* Authors:
|
||||
* Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi_utils/i2c/dw_i2c.h>
|
||||
|
||||
#define DW_IC_CON 0x00
|
||||
#define DW_IC_TAR 0x04
|
||||
#define DW_IC_SAR 0x08
|
||||
#define DW_IC_DATA_CMD 0x10
|
||||
#define DW_IC_SS_SCL_HCNT 0x14
|
||||
#define DW_IC_SS_SCL_LCNT 0x18
|
||||
#define DW_IC_FS_SCL_HCNT 0x1c
|
||||
#define DW_IC_FS_SCL_LCNT 0x20
|
||||
#define DW_IC_HS_SCL_HCNT 0x24
|
||||
#define DW_IC_HS_SCL_LCNT 0x28
|
||||
#define DW_IC_INTR_STAT 0x2c
|
||||
#define DW_IC_INTR_MASK 0x30
|
||||
#define DW_IC_RAW_INTR_STAT 0x34
|
||||
#define DW_IC_RX_TL 0x38
|
||||
#define DW_IC_TX_TL 0x3c
|
||||
#define DW_IC_CLR_INTR 0x40
|
||||
#define DW_IC_CLR_RX_UNDER 0x44
|
||||
#define DW_IC_CLR_RX_OVER 0x48
|
||||
#define DW_IC_CLR_TX_OVER 0x4c
|
||||
#define DW_IC_CLR_RD_REQ 0x50
|
||||
#define DW_IC_CLR_TX_ABRT 0x54
|
||||
#define DW_IC_CLR_RX_DONE 0x58
|
||||
#define DW_IC_CLR_ACTIVITY 0x5c
|
||||
#define DW_IC_CLR_STOP_DET 0x60
|
||||
#define DW_IC_CLR_START_DET 0x64
|
||||
#define DW_IC_CLR_GEN_CALL 0x68
|
||||
#define DW_IC_ENABLE 0x6c
|
||||
#define DW_IC_STATUS 0x70
|
||||
#define DW_IC_TXFLR 0x74
|
||||
#define DW_IC_RXFLR 0x78
|
||||
#define DW_IC_SDA_HOLD 0x7c
|
||||
#define DW_IC_TX_ABRT_SOURCE 0x80
|
||||
#define DW_IC_ENABLE_STATUS 0x9c
|
||||
#define DW_IC_CLR_RESTART_DET 0xa8
|
||||
#define DW_IC_COMP_PARAM_1 0xf4
|
||||
#define DW_IC_COMP_VERSION 0xf8
|
||||
|
||||
#define DW_I2C_STATUS_TXFIFO_EMPTY BIT(2)
|
||||
#define DW_I2C_STATUS_RXFIFO_NOT_EMPTY BIT(3)
|
||||
|
||||
#define IC_DATA_CMD_READ BIT(8)
|
||||
#define IC_DATA_CMD_STOP BIT(9)
|
||||
#define IC_DATA_CMD_RESTART BIT(10)
|
||||
#define IC_INT_STATUS_STOPDET BIT(9)
|
||||
|
||||
static inline void dw_i2c_setreg(struct dw_i2c_adapter *adap,
|
||||
u8 reg, u32 value)
|
||||
{
|
||||
writel(value, (void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static inline u32 dw_i2c_getreg(struct dw_i2c_adapter *adap,
|
||||
u32 reg)
|
||||
{
|
||||
return readl((void *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_poll(struct dw_i2c_adapter *adap,
|
||||
u32 mask, u32 addr,
|
||||
bool inverted)
|
||||
{
|
||||
unsigned int timeout = 10; /* msec */
|
||||
int count = 0;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = dw_i2c_getreg(adap, addr);
|
||||
if (inverted) {
|
||||
if (!(val & mask))
|
||||
return 0;
|
||||
} else {
|
||||
if (val & mask)
|
||||
return 0;
|
||||
}
|
||||
sbi_timer_udelay(2);
|
||||
count += 1;
|
||||
if (count == (timeout * 1000))
|
||||
return SBI_ETIMEDOUT;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
#define dw_i2c_adapter_poll_rxrdy(adap) \
|
||||
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_RXFIFO_NOT_EMPTY, DW_IC_STATUS, 0)
|
||||
#define dw_i2c_adapter_poll_txfifo_ready(adap) \
|
||||
dw_i2c_adapter_poll(adap, DW_I2C_STATUS_TXFIFO_EMPTY, DW_IC_STATUS, 0)
|
||||
|
||||
static int dw_i2c_write_addr(struct dw_i2c_adapter *adap, u8 addr)
|
||||
{
|
||||
dw_i2c_setreg(adap, DW_IC_ENABLE, 0);
|
||||
dw_i2c_setreg(adap, DW_IC_TAR, addr);
|
||||
dw_i2c_setreg(adap, DW_IC_ENABLE, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_read(struct i2c_adapter *ia, u8 addr,
|
||||
u8 reg, u8 *buffer, int len)
|
||||
{
|
||||
struct dw_i2c_adapter *adap =
|
||||
container_of(ia, struct dw_i2c_adapter, adapter);
|
||||
int rc;
|
||||
|
||||
dw_i2c_write_addr(adap, addr);
|
||||
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set register address */
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
|
||||
|
||||
/* set value */
|
||||
while (len) {
|
||||
if (len == 1)
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD,
|
||||
IC_DATA_CMD_READ | IC_DATA_CMD_STOP);
|
||||
else
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, IC_DATA_CMD_READ);
|
||||
|
||||
rc = dw_i2c_adapter_poll_rxrdy(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*buffer = dw_i2c_getreg(adap, DW_IC_DATA_CMD) & 0xff;
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_i2c_adapter_write(struct i2c_adapter *ia, u8 addr,
|
||||
u8 reg, u8 *buffer, int len)
|
||||
{
|
||||
struct dw_i2c_adapter *adap =
|
||||
container_of(ia, struct dw_i2c_adapter, adapter);
|
||||
int rc;
|
||||
|
||||
dw_i2c_write_addr(adap, addr);
|
||||
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* set register address */
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, reg);
|
||||
|
||||
while (len) {
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (len == 1)
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer | IC_DATA_CMD_STOP);
|
||||
else
|
||||
dw_i2c_setreg(adap, DW_IC_DATA_CMD, *buffer);
|
||||
|
||||
buffer++;
|
||||
len--;
|
||||
}
|
||||
rc = dw_i2c_adapter_poll_txfifo_ready(adap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dw_i2c_init(struct i2c_adapter *adapter, int nodeoff)
|
||||
{
|
||||
adapter->id = nodeoff;
|
||||
adapter->write = dw_i2c_adapter_write;
|
||||
adapter->read = dw_i2c_adapter_read;
|
||||
|
||||
return i2c_adapter_add(adapter);
|
||||
}
|
||||
62
lib/utils/i2c/fdt_i2c_dw.c
Normal file
62
lib/utils/i2c/fdt_i2c_dw.c
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 starfivetech.com
|
||||
*
|
||||
* Authors:
|
||||
* Minda Chen <minda.chen@starfivetech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/i2c/dw_i2c.h>
|
||||
#include <sbi_utils/i2c/fdt_i2c.h>
|
||||
|
||||
#define FDT_DW_I2C_ADAPTER_MAX 7
|
||||
|
||||
static unsigned int fdt_dw_i2c_adapter_count;
|
||||
static struct dw_i2c_adapter
|
||||
fdt_dw_i2c_adapter_array[FDT_DW_I2C_ADAPTER_MAX];
|
||||
|
||||
extern struct fdt_i2c_adapter fdt_i2c_adapter_dw;
|
||||
|
||||
static int fdt_dw_i2c_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct dw_i2c_adapter *adapter;
|
||||
u64 addr;
|
||||
|
||||
if (fdt_dw_i2c_adapter_count >= FDT_DW_I2C_ADAPTER_MAX)
|
||||
return SBI_ENOSPC;
|
||||
|
||||
adapter = &fdt_dw_i2c_adapter_array[fdt_dw_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_dw;
|
||||
|
||||
rc = dw_i2c_init(&adapter->adapter, nodeoff);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
fdt_dw_i2c_adapter_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match fdt_dw_i2c_match[] = {
|
||||
{ .compatible = "snps,designware-i2c" },
|
||||
{ .compatible = "starfive,jh7110-i2c" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_i2c_adapter fdt_i2c_adapter_dw = {
|
||||
.match_table = fdt_dw_i2c_match,
|
||||
.init = fdt_dw_i2c_init,
|
||||
};
|
||||
@@ -14,3 +14,8 @@ libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
|
||||
|
||||
carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
|
||||
libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
|
||||
|
||||
carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_DW) += fdt_i2c_adapter_dw
|
||||
libsbiutils-objs-$(CONFIG_FDT_I2C_DW) += i2c/fdt_i2c_dw.o
|
||||
|
||||
libsbiutils-objs-$(CONFIG_I2C_DW) += i2c/dw_i2c.o
|
||||
|
||||
@@ -88,7 +88,10 @@ int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
|
||||
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, ®);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE),
|
||||
®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@@ -57,6 +57,8 @@ static const struct fdt_match ipi_mswi_match[] = {
|
||||
{ .compatible = "riscv,clint0", .data = &clint_offset },
|
||||
{ .compatible = "sifive,clint0", .data = &clint_offset },
|
||||
{ .compatible = "thead,c900-clint", .data = &clint_offset },
|
||||
{ .compatible = "sophgo,sg2042-clint-mswi", .data = &clint_offset },
|
||||
{ .compatible = "thead,c900-clint-mswi", .data = &clint_offset },
|
||||
{ .compatible = "riscv,aclint-mswi" },
|
||||
{ },
|
||||
};
|
||||
|
||||
@@ -269,7 +269,10 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic)
|
||||
(last_deleg_irq == aplic->num_source) &&
|
||||
(first_deleg_irq == 1))) {
|
||||
sbi_domain_memregion_init(aplic->addr, aplic->size,
|
||||
SBI_DOMAIN_MEMREGION_MMIO, ®);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE),
|
||||
®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@@ -313,7 +313,10 @@ int imsic_cold_irqchip_init(struct imsic_data *imsic)
|
||||
for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) {
|
||||
sbi_domain_memregion_init(imsic->regs[i].addr,
|
||||
imsic->regs[i].size,
|
||||
SBI_DOMAIN_MEMREGION_MMIO, ®);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE),
|
||||
®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_byteorder.h>
|
||||
|
||||
#define INT_MAX ((int)(~0U >> 1))
|
||||
#define UINT_MAX ((unsigned int)~0U)
|
||||
@@ -37,49 +38,39 @@
|
||||
#define strlen sbi_strlen
|
||||
#define strnlen sbi_strnlen
|
||||
|
||||
typedef uint16_t FDT_BITWISE fdt16_t;
|
||||
typedef uint32_t FDT_BITWISE fdt32_t;
|
||||
typedef uint64_t FDT_BITWISE fdt64_t;
|
||||
|
||||
#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
|
||||
#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
|
||||
#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
|
||||
(EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
|
||||
#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
|
||||
(EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
|
||||
(EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
|
||||
(EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
|
||||
typedef be16_t FDT_BITWISE fdt16_t;
|
||||
typedef be32_t FDT_BITWISE fdt32_t;
|
||||
typedef be64_t FDT_BITWISE fdt64_t;
|
||||
|
||||
static inline uint16_t fdt16_to_cpu(fdt16_t x)
|
||||
{
|
||||
return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
|
||||
return (FDT_FORCE uint16_t)be16_to_cpu(x);
|
||||
}
|
||||
|
||||
static inline fdt16_t cpu_to_fdt16(uint16_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
|
||||
return (FDT_FORCE fdt16_t)cpu_to_be16(x);
|
||||
}
|
||||
|
||||
static inline uint32_t fdt32_to_cpu(fdt32_t x)
|
||||
{
|
||||
return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
|
||||
return (FDT_FORCE uint32_t)be32_to_cpu(x);
|
||||
}
|
||||
|
||||
static inline fdt32_t cpu_to_fdt32(uint32_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
|
||||
return (FDT_FORCE fdt32_t)cpu_to_be32(x);
|
||||
}
|
||||
|
||||
static inline uint64_t fdt64_to_cpu(fdt64_t x)
|
||||
{
|
||||
return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
|
||||
return (FDT_FORCE uint64_t)be64_to_cpu(x);
|
||||
}
|
||||
|
||||
static inline fdt64_t cpu_to_fdt64(uint64_t x)
|
||||
{
|
||||
return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
|
||||
return (FDT_FORCE fdt64_t)cpu_to_be64(x);
|
||||
}
|
||||
#undef CPU_TO_FDT64
|
||||
#undef CPU_TO_FDT32
|
||||
#undef CPU_TO_FDT16
|
||||
#undef EXTRACT_BYTE
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
@@ -11,6 +11,7 @@ if FDT_RESET
|
||||
|
||||
config FDT_RESET_ATCWDT200
|
||||
bool "Andes WDT FDT reset driver"
|
||||
depends on SYS_ATCSMU
|
||||
default n
|
||||
|
||||
config FDT_RESET_GPIO
|
||||
@@ -28,6 +29,21 @@ config FDT_RESET_SIFIVE_TEST
|
||||
select SYS_SIFIVE_TEST
|
||||
default n
|
||||
|
||||
config FDT_RESET_SOPHGO_CPLD
|
||||
bool "Sophgo CPLD FDT reset driver"
|
||||
select SYS_SOPHGO_CPLD
|
||||
default n
|
||||
|
||||
config FDT_RESET_SOPHGO_MCU
|
||||
bool "Sophgo MCU FDT reset driver"
|
||||
select SYS_SOPHGO_MCU
|
||||
default n
|
||||
|
||||
config FDT_RESET_SOPHGO_WDT
|
||||
bool "Sophgo WDT FDT reset driver"
|
||||
select SYS_SOPHGO_WDT
|
||||
default n
|
||||
|
||||
config FDT_RESET_SUNXI_WDT
|
||||
bool "Sunxi WDT FDT reset driver"
|
||||
default n
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/reset/fdt_reset.h>
|
||||
#include <sbi_utils/sys/atcsmu.h>
|
||||
|
||||
#define ATCWDT200_WP_NUM 0x5aa5
|
||||
#define WREN_REG 0x18
|
||||
@@ -41,14 +42,8 @@
|
||||
#define CLK_PCLK (1 << 1)
|
||||
#define WDT_EN (1 << 0)
|
||||
|
||||
#define FLASH_BASE 0x80000000ULL
|
||||
#define SMU_RESET_VEC_LO_OFF 0x50
|
||||
#define SMU_RESET_VEC_HI_OFF 0x60
|
||||
#define SMU_HARTn_RESET_VEC_LO(n) (SMU_RESET_VEC_LO_OFF + (n * 0x4))
|
||||
#define SMU_HARTn_RESET_VEC_HI(n) (SMU_RESET_VEC_HI_OFF + (n * 0x4))
|
||||
|
||||
static volatile char *wdt_addr;
|
||||
static volatile char *smu_addr;
|
||||
static volatile char *wdt_addr = NULL;
|
||||
static struct smu_data smu = { 0 };
|
||||
|
||||
static int ae350_system_reset_check(u32 type, u32 reason)
|
||||
{
|
||||
@@ -66,16 +61,16 @@ static void ae350_system_reset(u32 type, u32 reason)
|
||||
{
|
||||
const struct sbi_platform *plat = sbi_platform_thishart_ptr();
|
||||
|
||||
for (int i = 0; i < sbi_platform_hart_count(plat); i++) {
|
||||
writel(FLASH_BASE, smu_addr + SMU_HARTn_RESET_VEC_LO(i));
|
||||
writel(FLASH_BASE >> 32, smu_addr + SMU_HARTn_RESET_VEC_HI(i));
|
||||
}
|
||||
for (int i = 0; i < sbi_platform_hart_count(plat); i++)
|
||||
if (smu_set_reset_vector(&smu, FLASH_BASE, i))
|
||||
goto fail;
|
||||
|
||||
/* Program WDT control register */
|
||||
writew(ATCWDT200_WP_NUM, wdt_addr + WREN_REG);
|
||||
writel(INT_CLK_32768 | INT_EN | RST_CLK_128 | RST_EN | WDT_EN,
|
||||
wdt_addr + CTRL_REG);
|
||||
|
||||
fail:
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
@@ -104,7 +99,7 @@ static int atcwdt200_reset_init(void *fdt, int nodeoff,
|
||||
if (fdt_parse_compat_addr(fdt, ®_addr, "andestech,atcsmu"))
|
||||
return SBI_ENODEV;
|
||||
|
||||
smu_addr = (volatile char *)(unsigned long)reg_addr;
|
||||
smu.addr = (unsigned long)reg_addr;
|
||||
|
||||
sbi_system_reset_add_device(&atcwdt200_reset);
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ static void gpio_reset_exec(struct gpio_reset *reset)
|
||||
|
||||
static int gpio_system_poweroff_check(u32 type, u32 reason)
|
||||
{
|
||||
if (gpio_reset_get(FALSE, type))
|
||||
if (gpio_reset_get(false, type))
|
||||
return 128;
|
||||
|
||||
return 0;
|
||||
@@ -85,7 +85,7 @@ static int gpio_system_poweroff_check(u32 type, u32 reason)
|
||||
|
||||
static void gpio_system_poweroff(u32 type, u32 reason)
|
||||
{
|
||||
gpio_reset_exec(gpio_reset_get(FALSE, type));
|
||||
gpio_reset_exec(gpio_reset_get(false, type));
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device gpio_poweroff = {
|
||||
@@ -96,7 +96,7 @@ static struct sbi_system_reset_device gpio_poweroff = {
|
||||
|
||||
static int gpio_system_restart_check(u32 type, u32 reason)
|
||||
{
|
||||
if (gpio_reset_get(TRUE, type))
|
||||
if (gpio_reset_get(true, type))
|
||||
return 128;
|
||||
|
||||
return 0;
|
||||
@@ -104,7 +104,7 @@ static int gpio_system_restart_check(u32 type, u32 reason)
|
||||
|
||||
static void gpio_system_restart(u32 type, u32 reason)
|
||||
{
|
||||
gpio_reset_exec(gpio_reset_get(TRUE, type));
|
||||
gpio_reset_exec(gpio_reset_get(true, type));
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device gpio_restart = {
|
||||
@@ -149,7 +149,7 @@ static int gpio_reset_init(void *fdt, int nodeoff,
|
||||
}
|
||||
|
||||
static const struct fdt_match gpio_poweroff_match[] = {
|
||||
{ .compatible = "gpio-poweroff", .data = (const void *)FALSE },
|
||||
{ .compatible = "gpio-poweroff", .data = (const void *)false },
|
||||
{ },
|
||||
};
|
||||
|
||||
@@ -159,7 +159,7 @@ struct fdt_reset fdt_poweroff_gpio = {
|
||||
};
|
||||
|
||||
static const struct fdt_match gpio_reset_match[] = {
|
||||
{ .compatible = "gpio-restart", .data = (const void *)TRUE },
|
||||
{ .compatible = "gpio-restart", .data = (const void *)true },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
167
lib/utils/reset/fdt_reset_sophgo_cpld.c
Normal file
167
lib/utils/reset/fdt_reset_sophgo_cpld.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Sophgo
|
||||
*
|
||||
* Authors:
|
||||
* Chunzhi Lin <chunzhi.lin@sophgo.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
#include <sbi_utils/reset/fdt_reset.h>
|
||||
|
||||
struct cpld_reset {
|
||||
struct gpio_pin pin;
|
||||
u32 active_delay;
|
||||
u32 inactive_delay;
|
||||
};
|
||||
|
||||
static struct cpld_reset poweroff = {
|
||||
.active_delay = 300,
|
||||
.inactive_delay = 300
|
||||
};
|
||||
|
||||
static struct cpld_reset reboot = {
|
||||
.active_delay = 300,
|
||||
.inactive_delay = 300
|
||||
};
|
||||
|
||||
static struct cpld_reset *cpld_reset_get(bool is_poweroff, u32 type)
|
||||
{
|
||||
struct cpld_reset *reset = NULL;
|
||||
|
||||
switch (type) {
|
||||
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
||||
if (is_poweroff)
|
||||
reset = &poweroff;
|
||||
break;
|
||||
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
||||
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
||||
if (!is_poweroff)
|
||||
reset = &reboot;
|
||||
break;
|
||||
}
|
||||
|
||||
if (reset && !reset->pin.chip)
|
||||
reset = NULL;
|
||||
|
||||
return reset;
|
||||
}
|
||||
|
||||
static void cpld_reset_exec(struct cpld_reset *reset)
|
||||
{
|
||||
if (reset) {
|
||||
/* drive it active, also inactive->active edge */
|
||||
gpio_direction_output(&reset->pin, 1);
|
||||
sbi_timer_mdelay(reset->active_delay);
|
||||
|
||||
/* drive inactive, also active->inactive edge */
|
||||
gpio_set(&reset->pin, 0);
|
||||
sbi_timer_mdelay(reset->inactive_delay);
|
||||
}
|
||||
/* hang !!! */
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
static int cpld_system_poweroff_check(u32 type, u32 reason)
|
||||
{
|
||||
if (cpld_reset_get(true, type))
|
||||
return 128;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpld_system_poweroff(u32 type, u32 reason)
|
||||
{
|
||||
cpld_reset_exec(cpld_reset_get(true, type));
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device mango_reset_gpio_poweroff = {
|
||||
.name = "mango-cpld",
|
||||
.system_reset_check = cpld_system_poweroff_check,
|
||||
.system_reset = cpld_system_poweroff
|
||||
};
|
||||
|
||||
static int cpld_system_reboot_check(u32 type, u32 reason)
|
||||
{
|
||||
if (cpld_reset_get(false, type))
|
||||
return 128;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpld_system_reboot(u32 type, u32 reason)
|
||||
{
|
||||
cpld_reset_exec(cpld_reset_get(false, type));
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device mango_reset_gpio_reboot = {
|
||||
.name = "mango-cpld",
|
||||
.system_reset_check = cpld_system_reboot_check,
|
||||
.system_reset = cpld_system_reboot
|
||||
};
|
||||
|
||||
static int mango_cpld_reset_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc, len;
|
||||
const fdt32_t *val;
|
||||
bool is_poweroff = (ulong)match->data;
|
||||
struct cpld_reset *reset = (is_poweroff) ? &poweroff : &reboot;
|
||||
const char *dir_prop = "output";
|
||||
|
||||
rc = fdt_gpio_pin_get(fdt, nodeoff, 0, &reset->pin);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
if (fdt_getprop(fdt, nodeoff, dir_prop, &len)) {
|
||||
rc = gpio_direction_output(&reset->pin, 0);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "active-delay-ms", &len);
|
||||
if (len > 0)
|
||||
reset->active_delay = fdt32_to_cpu(*val);
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "inactive-delay-ms", &len);
|
||||
if (len > 0)
|
||||
reset->inactive_delay = fdt32_to_cpu(*val);
|
||||
|
||||
if (is_poweroff)
|
||||
sbi_system_reset_add_device(&mango_reset_gpio_poweroff);
|
||||
else
|
||||
sbi_system_reset_add_device(&mango_reset_gpio_reboot);
|
||||
|
||||
out:
|
||||
fdt_del_node(fdt, nodeoff);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct fdt_match mango_cpld_poweroff_match[] = {
|
||||
{ .compatible = "mango,cpld-poweroff", .data = (void *)true},
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_reset fdt_reset_sophgo_cpld_poweroff = {
|
||||
.match_table = mango_cpld_poweroff_match,
|
||||
.init = mango_cpld_reset_init,
|
||||
};
|
||||
|
||||
static const struct fdt_match mango_cpld_reboot_match[] = {
|
||||
{ .compatible = "mango,cpld-reboot", .data = (void *)false},
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_reset fdt_reset_sophgo_cpld_reboot = {
|
||||
.match_table = mango_cpld_reboot_match,
|
||||
.init = mango_cpld_reset_init,
|
||||
};
|
||||
157
lib/utils/reset/fdt_reset_sophgo_mcu.c
Normal file
157
lib/utils/reset/fdt_reset_sophgo_mcu.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Lin Chunzhi <chunzhi.lin@sophgo.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/reset/fdt_reset.h>
|
||||
#include <sbi_utils/i2c/fdt_i2c.h>
|
||||
|
||||
#define MANGO_BOARD_TYPE 0x80
|
||||
#define MANGO_BOARD_TYPE_MASK 1 << 7
|
||||
|
||||
#define REG_MCU_BOARD_TYPE 0x00
|
||||
#define REG_MCU_CMD 0x03
|
||||
|
||||
#define CMD_POWEROFF 0x02
|
||||
#define CMD_RESET 0x03
|
||||
#define CMD_REBOOT 0x07
|
||||
|
||||
static struct {
|
||||
struct i2c_adapter *adapter;
|
||||
uint32_t reg;
|
||||
} mango;
|
||||
|
||||
static int mango_system_reset_check(u32 type, u32 reason)
|
||||
{
|
||||
switch (type) {
|
||||
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
||||
return 1;
|
||||
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
||||
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
||||
return 255;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mango_sanity_check(struct i2c_adapter *adap, uint32_t reg)
|
||||
{
|
||||
static uint8_t val;
|
||||
int ret;
|
||||
|
||||
/* check board type*/
|
||||
ret = i2c_adapter_reg_read(adap, reg, REG_MCU_BOARD_TYPE, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((val & MANGO_BOARD_TYPE_MASK) != MANGO_BOARD_TYPE)
|
||||
return SBI_ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mango_shutdown(struct i2c_adapter *adap, uint32_t reg)
|
||||
{
|
||||
int ret;
|
||||
ret = i2c_adapter_reg_write(adap, reg, REG_MCU_CMD, CMD_POWEROFF);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mango_reset(struct i2c_adapter *adap, uint32_t reg)
|
||||
{
|
||||
int ret;
|
||||
ret = i2c_adapter_reg_write(adap, reg, REG_MCU_CMD, CMD_REBOOT);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mango_system_reset(u32 type, u32 reason)
|
||||
{
|
||||
struct i2c_adapter *adap = mango.adapter;
|
||||
uint32_t reg = mango.reg;
|
||||
int ret;
|
||||
if (adap) {
|
||||
/* sanity check */
|
||||
ret = mango_sanity_check(adap, reg);
|
||||
if (ret) {
|
||||
sbi_printf("%s: chip is not mango\n", __func__);
|
||||
goto skip_reset;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SBI_SRST_RESET_TYPE_SHUTDOWN:
|
||||
mango_shutdown(adap, reg);
|
||||
break;
|
||||
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
||||
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
||||
mango_reset(adap, reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_reset:
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device mango_reset_i2c = {
|
||||
.name = "mango-reset",
|
||||
.system_reset_check = mango_system_reset_check,
|
||||
.system_reset = mango_system_reset
|
||||
};
|
||||
|
||||
static int mango_reset_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc, i2c_bus;
|
||||
struct i2c_adapter *adapter;
|
||||
uint64_t addr;
|
||||
|
||||
/* we are mango,mcu node */
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mango.reg = addr;
|
||||
|
||||
/* find i2c bus parent node */
|
||||
i2c_bus = fdt_parent_offset(fdt, nodeoff);
|
||||
if (i2c_bus < 0)
|
||||
return i2c_bus;
|
||||
|
||||
/* i2c adapter get */
|
||||
rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mango.adapter = adapter;
|
||||
|
||||
sbi_system_reset_add_device(&mango_reset_i2c);
|
||||
fdt_del_node(fdt, nodeoff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match mango_reset_match[] = {
|
||||
{ .compatible = "mango,reset", .data = (void *)true},
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_reset fdt_reset_sophgo_mcu = {
|
||||
.match_table = mango_reset_match,
|
||||
.init = mango_reset_init,
|
||||
};
|
||||
123
lib/utils/reset/fdt_reset_sophgo_wdt.c
Normal file
123
lib/utils/reset/fdt_reset_sophgo_wdt.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Sophgo
|
||||
*
|
||||
* Authors:
|
||||
* Chunzhi Lin <chunzhi.lin@sophgo.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/reset/fdt_reset.h>
|
||||
|
||||
#define TOP_BASE 0x7030010000
|
||||
#define SOPHGO_TOP_CTRL_REG_OFFSET 0x008
|
||||
|
||||
#define REG_BIT(offset) (1U << (offset))
|
||||
#define BIT_MASK_TOP_CTRL_SW_ROOT_RESET_EN REG_BIT(2)
|
||||
|
||||
#define SOPHGO_WDT_CR_REG_OFFSET 0x00
|
||||
#define SOPHGO_WDT_TORR_REG_OFFSET 0x04
|
||||
#define SOPHGO_WDT_CRR_REG_OFFSET 0x0C
|
||||
|
||||
static volatile char *sophgo_wdt_base;
|
||||
static volatile char *sophgo_top_base;
|
||||
|
||||
static void sophgo_wdt_system_reset(u32 type, u32 reason)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(sophgo_top_base + SOPHGO_TOP_CTRL_REG_OFFSET);
|
||||
writel((val | BIT_MASK_TOP_CTRL_SW_ROOT_RESET_EN),
|
||||
sophgo_top_base + SOPHGO_TOP_CTRL_REG_OFFSET);
|
||||
sbi_timer_udelay(1);
|
||||
|
||||
/*next reset time = 2^(16 + 0x0b) / 100M = 1.34s */
|
||||
writel(0x0b, sophgo_wdt_base + SOPHGO_WDT_TORR_REG_OFFSET);
|
||||
sbi_timer_udelay(1);
|
||||
|
||||
/* a safety feature for counter restart register */
|
||||
writel(0x76, sophgo_wdt_base + SOPHGO_WDT_CRR_REG_OFFSET);
|
||||
sbi_timer_udelay(1);
|
||||
|
||||
/* reset pluse length: 32 pclk cycles; enable wdt */
|
||||
writel(0x11, sophgo_wdt_base + SOPHGO_WDT_CR_REG_OFFSET);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int sophgo_wdt_system_reset_check(u32 type, u32 reason)
|
||||
{
|
||||
switch (type) {
|
||||
case SBI_SRST_RESET_TYPE_COLD_REBOOT:
|
||||
case SBI_SRST_RESET_TYPE_WARM_REBOOT:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sophgo_wdt_system_get_top_base(void *fdt,
|
||||
int nodeoff, unsigned long *addr)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
int len, noff;
|
||||
|
||||
noff = 0;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "subctrl-syscon", &len);
|
||||
if (val || len >= sizeof(fdt32_t)) {
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
}
|
||||
|
||||
return fdt_get_node_addr_size(fdt, noff, 0, addr, NULL);
|
||||
}
|
||||
|
||||
static struct sbi_system_reset_device mango_reset_wdt = {
|
||||
.name = "mango-wdt",
|
||||
.system_reset_check = sophgo_wdt_system_reset_check,
|
||||
.system_reset = sophgo_wdt_system_reset,
|
||||
};
|
||||
|
||||
static int mango_wdt_reset_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
unsigned long wdt_addr, top_addr;
|
||||
int rc;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &wdt_addr, NULL);
|
||||
if (rc < 0 || !wdt_addr) {
|
||||
return SBI_ENODEV;
|
||||
}
|
||||
|
||||
sophgo_wdt_base = (volatile char *)(unsigned long)wdt_addr;
|
||||
|
||||
rc = sophgo_wdt_system_get_top_base(fdt, nodeoff, &top_addr);
|
||||
if (rc < 0 || !top_addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
sophgo_top_base = (volatile char *)(unsigned long)top_addr;
|
||||
|
||||
sbi_system_reset_add_device(&mango_reset_wdt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match mango_wdt_reset_match[] = {
|
||||
{ .compatible = "mango,wdt-reset", .data = (void *)true},
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_reset fdt_reset_sophgo_wdt = {
|
||||
.match_table = mango_wdt_reset_match,
|
||||
.init = mango_wdt_reset_init,
|
||||
};
|
||||
@@ -23,6 +23,16 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SIFIVE_TEST) += fdt_reset_sifive_test
|
||||
libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.o
|
||||
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SOPHGO_CPLD) += fdt_reset_sophgo_cpld_poweroff
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SOPHGO_CPLD) += fdt_reset_sophgo_cpld_reboot
|
||||
libsbiutils-objs-$(CONFIG_FDT_RESET_SOPHGO_CPLD) += reset/fdt_reset_sophgo_cpld.o
|
||||
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SOPHGO_MCU) += fdt_reset_sophgo_mcu
|
||||
libsbiutils-objs-$(CONFIG_FDT_RESET_SOPHGO_MCU) += reset/fdt_reset_sophgo_mcu.o
|
||||
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SOPHGO_WDT) += fdt_reset_sophgo_wdt
|
||||
libsbiutils-objs-$(CONFIG_FDT_RESET_SOPHGO_WDT) += reset/fdt_reset_sophgo_wdt.o
|
||||
|
||||
carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
|
||||
libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ static int serial_cadence_init(void *fdt, int nodeoff,
|
||||
}
|
||||
|
||||
static const struct fdt_match serial_cadence_match[] = {
|
||||
{ .compatible = "cdns,uart-r1p8", },
|
||||
{ .compatible = "cdns,uart-r1p12" },
|
||||
{ .compatible = "starfive,jh8100-uart" },
|
||||
{ },
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#define SYSOPEN 0x01
|
||||
#define SYSWRITEC 0x03
|
||||
#define SYSWRITE 0x05
|
||||
#define SYSREAD 0x06
|
||||
#define SYSREADC 0x07
|
||||
#define SYSERRNO 0x13
|
||||
@@ -93,6 +94,7 @@ static int semihosting_errno(void)
|
||||
}
|
||||
|
||||
static int semihosting_infd = SBI_ENODEV;
|
||||
static int semihosting_outfd = SBI_ENODEV;
|
||||
|
||||
static long semihosting_open(const char *fname, enum semihosting_open_mode mode)
|
||||
{
|
||||
@@ -141,6 +143,21 @@ static long semihosting_read(long fd, void *memp, size_t len)
|
||||
return len - ret;
|
||||
}
|
||||
|
||||
static long semihosting_write(long fd, const void *memp, size_t len)
|
||||
{
|
||||
long ret;
|
||||
struct semihosting_rdwr_s write;
|
||||
|
||||
write.fd = fd;
|
||||
write.memp = (void *)memp;
|
||||
write.len = len;
|
||||
|
||||
ret = semihosting_trap(SYSWRITE, &write);
|
||||
if (ret < 0)
|
||||
return semihosting_errno();
|
||||
return len - ret;
|
||||
}
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static void semihosting_putc(char ch)
|
||||
@@ -148,6 +165,24 @@ static void semihosting_putc(char ch)
|
||||
semihosting_trap(SYSWRITEC, &ch);
|
||||
}
|
||||
|
||||
static unsigned long semihosting_puts(const char *str, unsigned long len)
|
||||
{
|
||||
char ch;
|
||||
long ret;
|
||||
unsigned long i;
|
||||
|
||||
if (semihosting_outfd < 0) {
|
||||
for (i = 0; i < len; i++) {
|
||||
ch = str[i];
|
||||
semihosting_trap(SYSWRITEC, &ch);
|
||||
}
|
||||
ret = len;
|
||||
} else
|
||||
ret = semihosting_write(semihosting_outfd, str, len);
|
||||
|
||||
return (ret < 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int semihosting_getc(void)
|
||||
{
|
||||
char ch = 0;
|
||||
@@ -165,12 +200,14 @@ static int semihosting_getc(void)
|
||||
static struct sbi_console_device semihosting_console = {
|
||||
.name = "semihosting",
|
||||
.console_putc = semihosting_putc,
|
||||
.console_puts = semihosting_puts,
|
||||
.console_getc = semihosting_getc
|
||||
};
|
||||
|
||||
int semihosting_init(void)
|
||||
{
|
||||
semihosting_infd = semihosting_open(":tt", MODE_READ);
|
||||
semihosting_outfd = semihosting_open(":tt", MODE_WRITE);
|
||||
|
||||
sbi_console_set_device(&semihosting_console);
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
menu "System Device Support"
|
||||
|
||||
config SYS_ATCSMU
|
||||
bool "Andes System Management Unit (SMU) support"
|
||||
default n
|
||||
|
||||
config SYS_HTIF
|
||||
bool "Host transfere interface (HTIF) support"
|
||||
default n
|
||||
|
||||
92
lib/utils/sys/atcsmu.c
Normal file
92
lib/utils/sys/atcsmu.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Andes Technology Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yu Chien Peter Lin <peterlin@andestech.com>
|
||||
*/
|
||||
|
||||
#include <sbi_utils/sys/atcsmu.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
|
||||
inline int smu_set_wakeup_events(struct smu_data *smu, u32 events, u32 hartid)
|
||||
{
|
||||
if (smu) {
|
||||
writel(events, (void *)(smu->addr + PCSm_WE_OFFSET(hartid)));
|
||||
return 0;
|
||||
} else
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
inline bool smu_support_sleep_mode(struct smu_data *smu, u32 sleep_mode,
|
||||
u32 hartid)
|
||||
{
|
||||
u32 pcs_cfg;
|
||||
|
||||
if (!smu) {
|
||||
sbi_printf("%s(): Failed to access smu_data\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
pcs_cfg = readl((void *)(smu->addr + PCSm_CFG_OFFSET(hartid)));
|
||||
|
||||
switch (sleep_mode) {
|
||||
case LIGHTSLEEP_MODE:
|
||||
if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_LIGHT_SLEEP) == 0) {
|
||||
sbi_printf(
|
||||
"SMU: hart%d (PCS%d) does not support light sleep mode\n",
|
||||
hartid, hartid + 3);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DEEPSLEEP_MODE:
|
||||
if (EXTRACT_FIELD(pcs_cfg, PCS_CFG_DEEP_SLEEP) == 0) {
|
||||
sbi_printf(
|
||||
"SMU: hart%d (PCS%d) does not support deep sleep mode\n",
|
||||
hartid, hartid + 3);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int smu_set_command(struct smu_data *smu, u32 pcs_ctl, u32 hartid)
|
||||
{
|
||||
if (smu) {
|
||||
writel(pcs_ctl, (void *)(smu->addr + PCSm_CTL_OFFSET(hartid)));
|
||||
return 0;
|
||||
} else
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
inline int smu_set_reset_vector(struct smu_data *smu, ulong wakeup_addr,
|
||||
u32 hartid)
|
||||
{
|
||||
u32 vec_lo, vec_hi;
|
||||
u64 reset_vector;
|
||||
|
||||
if (!smu)
|
||||
return SBI_EINVAL;
|
||||
|
||||
writel(wakeup_addr, (void *)(smu->addr + HARTn_RESET_VEC_LO(hartid)));
|
||||
writel((u64)wakeup_addr >> 32,
|
||||
(void *)(smu->addr + HARTn_RESET_VEC_HI(hartid)));
|
||||
|
||||
vec_lo = readl((void *)(smu->addr + HARTn_RESET_VEC_LO(hartid)));
|
||||
vec_hi = readl((void *)(smu->addr + HARTn_RESET_VEC_HI(hartid)));
|
||||
reset_vector = ((u64)vec_hi << 32) | vec_lo;
|
||||
|
||||
if (reset_vector != (u64)wakeup_addr) {
|
||||
sbi_printf(
|
||||
"hard%d (PCS%d): Failed to program the reset vector.\n",
|
||||
hartid, hartid + 3);
|
||||
return SBI_EFAIL;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
@@ -135,11 +135,11 @@ static void do_tohost_fromhost(uint64_t dev, uint64_t cmd, uint64_t data)
|
||||
__set_tohost(HTIF_DEV_SYSTEM, cmd, data);
|
||||
|
||||
while (1) {
|
||||
uint64_t fh = fromhost;
|
||||
uint64_t fh = __read_fromhost();
|
||||
if (fh) {
|
||||
if (FROMHOST_DEV(fh) == HTIF_DEV_SYSTEM &&
|
||||
FROMHOST_CMD(fh) == cmd) {
|
||||
fromhost = 0;
|
||||
__write_fromhost(0);
|
||||
break;
|
||||
}
|
||||
__check_fromhost();
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
|
||||
libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
|
||||
libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o
|
||||
libsbiutils-objs-$(CONFIG_SYS_ATCSMU) += sys/atcsmu.o
|
||||
|
||||
@@ -19,6 +19,11 @@ config FDT_TIMER_PLMT
|
||||
select TIMER_PLMT
|
||||
default n
|
||||
|
||||
config FDT_TIMER_SG2042_GMT
|
||||
bool "SOPHGO SG2042 global mtimer FDT driver"
|
||||
select TIMER_SG2042_GMT
|
||||
default n
|
||||
|
||||
endif
|
||||
|
||||
config TIMER_MTIMER
|
||||
@@ -29,4 +34,8 @@ config TIMER_PLMT
|
||||
bool "Andes PLMT support"
|
||||
default n
|
||||
|
||||
config TIMER_SG2042_GMT
|
||||
bool "SOPHGO SG2042 GMT support"
|
||||
default n
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -188,26 +188,34 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt,
|
||||
rc = sbi_domain_root_add_memrange(mt->mtimecmp_addr,
|
||||
mt->mtime_size + mt->mtimecmp_size,
|
||||
MTIMER_REGION_ALIGN,
|
||||
SBI_DOMAIN_MEMREGION_MMIO);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE));
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (mt->mtimecmp_addr == (mt->mtime_addr + mt->mtime_size)) {
|
||||
rc = sbi_domain_root_add_memrange(mt->mtime_addr,
|
||||
mt->mtime_size + mt->mtimecmp_size,
|
||||
MTIMER_REGION_ALIGN,
|
||||
SBI_DOMAIN_MEMREGION_MMIO);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE));
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
} else if (!mt->use_extern_domain) {
|
||||
rc = sbi_domain_root_add_memrange(mt->mtime_addr,
|
||||
mt->mtime_size, MTIMER_REGION_ALIGN,
|
||||
SBI_DOMAIN_MEMREGION_MMIO);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sbi_domain_root_add_memrange(mt->mtimecmp_addr,
|
||||
mt->mtimecmp_size, MTIMER_REGION_ALIGN,
|
||||
SBI_DOMAIN_MEMREGION_MMIO);
|
||||
(SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_M_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_M_WRITABLE));
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
#include <sbi_utils/timer/fdt_timer.h>
|
||||
#include <sbi_utils/timer/aclint_mtimer.h>
|
||||
|
||||
#define MTIMER_MAX_NR 16
|
||||
#define MTIMER_MAX_NR 32
|
||||
|
||||
struct timer_mtimer_quirks {
|
||||
unsigned int mtime_offset;
|
||||
bool has_64bit_mmio;
|
||||
bool without_mtime;
|
||||
bool use_extern_domain;
|
||||
};
|
||||
|
||||
static unsigned long mtimer_count = 0;
|
||||
@@ -66,6 +67,7 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff,
|
||||
mt->mtimecmp_addr += quirks->mtime_offset;
|
||||
/* Apply additional CLINT quirks */
|
||||
mt->has_64bit_mmio = quirks->has_64bit_mmio;
|
||||
mt->use_extern_domain = quirks->use_extern_domain;
|
||||
} else { /* RISC-V ACLINT MTIMER */
|
||||
/* Set ACLINT MTIMER addresses */
|
||||
mt->mtime_addr = addr[0];
|
||||
@@ -129,10 +131,20 @@ static const struct timer_mtimer_quirks thead_clint_quirks = {
|
||||
.without_mtime = true,
|
||||
};
|
||||
|
||||
static const struct timer_mtimer_quirks thead_clint_sep_quirks = {
|
||||
.mtime_offset = CLINT_MTIMER_OFFSET,
|
||||
.without_mtime = true,
|
||||
.use_extern_domain = true,
|
||||
};
|
||||
|
||||
static const struct fdt_match timer_mtimer_match[] = {
|
||||
{ .compatible = "riscv,clint0", .data = &sifive_clint_quirks },
|
||||
{ .compatible = "sifive,clint0", .data = &sifive_clint_quirks },
|
||||
{ .compatible = "thead,c900-clint", .data = &thead_clint_quirks },
|
||||
{ .compatible = "thead,c900-clint-mtimer",
|
||||
.data = &thead_clint_sep_quirks },
|
||||
{ .compatible = "sophgo,sg2042-clint-mtimer",
|
||||
.data = &thead_clint_sep_quirks },
|
||||
{ .compatible = "riscv,aclint-mtimer" },
|
||||
{ },
|
||||
};
|
||||
|
||||
61
lib/utils/timer/fdt_timer_sg2042_gmt.c
Normal file
61
lib/utils/timer/fdt_timer_sg2042_gmt.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Andes Technology Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yu Chien Peter Lin <peterlin@andestech.com>
|
||||
*/
|
||||
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/timer/fdt_timer.h>
|
||||
#include <sbi_utils/timer/sg2042_gmt.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <libfdt.h>
|
||||
|
||||
static int fdt_sg2042gmt_cold_timer_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int err;
|
||||
unsigned long mtimer_base, mtimer_size,
|
||||
mtimecmp_base, mtimecmp_size,
|
||||
declared_freq, actual_freq, timecmp_freq;
|
||||
const fdt32_t *val;
|
||||
int len;
|
||||
|
||||
err = fdt_get_node_addr_size(fdt, nodeoff, 0, &mtimer_base, &mtimer_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = fdt_get_node_addr_size(fdt, nodeoff, 1, &mtimecmp_base, &mtimecmp_size);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
err = fdt_parse_timebase_frequency(fdt, &declared_freq);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (fdt32_t *)fdt_getprop(fdt, nodeoff, "clock-frequency", &len);
|
||||
if (len < 8 || val == NULL)
|
||||
return SBI_EINVAL;
|
||||
|
||||
actual_freq = fdt32_to_cpu(*val);
|
||||
timecmp_freq = fdt32_to_cpu(*(val + 1));
|
||||
|
||||
return sg2042gmt_cold_timer_init(mtimer_base,
|
||||
mtimecmp_base, mtimecmp_size,
|
||||
declared_freq, actual_freq, timecmp_freq);
|
||||
}
|
||||
|
||||
static const struct fdt_match timer_sg2042gmt_match[] = {
|
||||
{ .compatible = "sophgo,sg2042-global-mtimer" },
|
||||
{},
|
||||
};
|
||||
|
||||
struct fdt_timer fdt_timer_sg2042_gmt = {
|
||||
.match_table = timer_sg2042gmt_match,
|
||||
.cold_init = fdt_sg2042gmt_cold_timer_init,
|
||||
.warm_init = sg2042gmt_warm_timer_init,
|
||||
.exit = NULL,
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o
|
||||
libsbiutils-objs-$(CONFIG_TIMER_PLMT) += timer/andes_plmt.o
|
||||
libsbiutils-objs-$(CONFIG_TIMER_SG2042_GMT) += timer/sg2042_gmt.o
|
||||
|
||||
libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o
|
||||
libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o
|
||||
@@ -18,3 +19,6 @@ libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o
|
||||
|
||||
carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_PLMT) += fdt_timer_plmt
|
||||
libsbiutils-objs-$(CONFIG_FDT_TIMER_PLMT) += timer/fdt_timer_plmt.o
|
||||
|
||||
carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_SG2042_GMT) += fdt_timer_sg2042_gmt
|
||||
libsbiutils-objs-$(CONFIG_FDT_TIMER_SG2042_GMT) += timer/fdt_timer_sg2042_gmt.o
|
||||
|
||||
135
lib/utils/timer/sg2042_gmt.c
Normal file
135
lib/utils/timer/sg2042_gmt.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Andes Technology Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yu Chien Peter Lin <peterlin@andestech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_timer.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/timer/sg2042_gmt.h>
|
||||
|
||||
struct sg2042gmt_data {
|
||||
unsigned long mtimer_base;
|
||||
unsigned long mtimecmp_base;
|
||||
unsigned long mtimecmp_size;
|
||||
unsigned long declared_freq;
|
||||
unsigned long actual_freq;
|
||||
unsigned long timecmp_freq;
|
||||
} sg2042gmt;
|
||||
|
||||
#define SG2042GMT_TIMECMP_CLUSTER_SIZE 0x10000
|
||||
#define SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT 4
|
||||
|
||||
static void *timecmp_addr(unsigned int hart_id)
|
||||
{
|
||||
unsigned int cluster_id = hart_id / SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT;
|
||||
unsigned int cluster_offset = hart_id % SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT;
|
||||
|
||||
return (void *)(sg2042gmt.mtimecmp_base + 0x4000 +
|
||||
cluster_id * SG2042GMT_TIMECMP_CLUSTER_SIZE +
|
||||
cluster_offset * 8);
|
||||
}
|
||||
|
||||
static u64 sg2042gmt_timer_value(void)
|
||||
{
|
||||
u64 global_timer = readq_relaxed((void *)sg2042gmt.mtimer_base);
|
||||
|
||||
/* convert global timer to declared frequency */
|
||||
return global_timer / (sg2042gmt.actual_freq / sg2042gmt.declared_freq);
|
||||
}
|
||||
|
||||
static void sg2042gmt_timer_event_stop(void)
|
||||
{
|
||||
unsigned int hart_id = current_hartid();
|
||||
|
||||
writel_relaxed(-1U, timecmp_addr(hart_id));
|
||||
writel_relaxed(-1U, timecmp_addr(hart_id) + 4);
|
||||
}
|
||||
|
||||
static void sg2042gmt_timer_event_start(u64 next_event)
|
||||
{
|
||||
unsigned int hart_id = current_hartid();
|
||||
|
||||
u64 global_timer = readq_relaxed((void *)sg2042gmt.mtimer_base);
|
||||
u64 clint_timer = csr_read(CSR_TIME);
|
||||
u64 delta_global_timer, delta_clint_timer;
|
||||
|
||||
delta_global_timer =
|
||||
(next_event * (sg2042gmt.actual_freq / sg2042gmt.declared_freq)) - global_timer;
|
||||
|
||||
delta_clint_timer = delta_global_timer * (sg2042gmt.timecmp_freq / sg2042gmt.actual_freq);
|
||||
|
||||
next_event = clint_timer + delta_clint_timer;
|
||||
|
||||
writel_relaxed(next_event >> 32, timecmp_addr(hart_id) + 4);
|
||||
writel_relaxed(next_event & 0xffffffff, timecmp_addr(hart_id));
|
||||
}
|
||||
|
||||
static struct sbi_timer_device sg2042gmt_timer = {
|
||||
.name = "sg2042gmt",
|
||||
.timer_freq = 50000000,
|
||||
.timer_value = sg2042gmt_timer_value,
|
||||
.timer_event_start = sg2042gmt_timer_event_start,
|
||||
.timer_event_stop = sg2042gmt_timer_event_stop
|
||||
};
|
||||
|
||||
int sg2042gmt_cold_timer_init(unsigned long mtimer_base,
|
||||
unsigned long mtimecmp_base,
|
||||
unsigned long mtimecmp_size,
|
||||
unsigned long declared_freq,
|
||||
unsigned long actual_freq,
|
||||
unsigned long timecmp_freq)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Add SG2042 GMT timer and time compare region to the root domain */
|
||||
#if 0
|
||||
err = sbi_domain_root_add_memrange(
|
||||
mtimer_base, 4096, 4096,
|
||||
SBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_READABLE);
|
||||
if (err) {
|
||||
sbi_printf("add timer to root domain failed\n");
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = sbi_domain_root_add_memrange(
|
||||
mtimecmp_base, mtimecmp_size, 4096,
|
||||
SBI_DOMAIN_MEMREGION_MMIO |
|
||||
SBI_DOMAIN_MEMREGION_READABLE |
|
||||
SBI_DOMAIN_MEMREGION_WRITEABLE);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
sg2042gmt_timer.timer_freq = declared_freq;
|
||||
|
||||
sg2042gmt.mtimer_base = mtimer_base;
|
||||
sg2042gmt.mtimecmp_base = mtimecmp_base;
|
||||
sg2042gmt.mtimecmp_size = mtimecmp_size;
|
||||
sg2042gmt.declared_freq = declared_freq;
|
||||
sg2042gmt.actual_freq = actual_freq;
|
||||
sg2042gmt.timecmp_freq = timecmp_freq;
|
||||
|
||||
sbi_timer_set_device(&sg2042gmt_timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sg2042gmt_warm_timer_init(void)
|
||||
{
|
||||
if (!sg2042gmt.mtimer_base)
|
||||
return SBI_ENODEV;
|
||||
|
||||
sg2042gmt_timer_event_stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ static struct aclint_mtimer_data mtimer = {
|
||||
.mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE,
|
||||
.first_hartid = 0,
|
||||
.hart_count = ARIANE_HART_COUNT,
|
||||
.has_64bit_mmio = TRUE,
|
||||
.has_64bit_mmio = true,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -60,7 +60,7 @@ static struct aclint_mtimer_data mtimer = {
|
||||
.mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE,
|
||||
.first_hartid = 0,
|
||||
.hart_count = OPENPITON_DEFAULT_HART_COUNT,
|
||||
.has_64bit_mmio = TRUE,
|
||||
.has_64bit_mmio = true,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -30,10 +30,12 @@ config PLATFORM_ALLWINNER_D1
|
||||
|
||||
config PLATFORM_ANDES_AE350
|
||||
bool "Andes AE350 support"
|
||||
select SYS_ATCSMU
|
||||
default n
|
||||
|
||||
config PLATFORM_RENESAS_RZFIVE
|
||||
bool "Renesas RZ/Five support"
|
||||
select ANDES45_PMA
|
||||
default n
|
||||
|
||||
config PLATFORM_SIFIVE_FU540
|
||||
@@ -45,4 +47,14 @@ config PLATFORM_SIFIVE_FU740
|
||||
depends on FDT_RESET && FDT_I2C
|
||||
default n
|
||||
|
||||
config PLATFORM_STARFIVE_JH7110
|
||||
bool "StarFive JH7110 support"
|
||||
default n
|
||||
|
||||
config PLATFORM_SOPHGO_MANGO
|
||||
bool "Sophgo MANGO support"
|
||||
default n
|
||||
|
||||
source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
|
||||
#include <platform_override.h>
|
||||
#include <thead_c9xx.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_ecall_interface.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_fixup.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip_plic.h>
|
||||
|
||||
@@ -69,10 +72,10 @@ static void sun20i_d1_csr_restore(void)
|
||||
* PLIC
|
||||
*/
|
||||
|
||||
#define PLIC_SOURCES 176
|
||||
#define PLIC_IE_WORDS ((PLIC_SOURCES + 31) / 32)
|
||||
#define PLIC_SOURCES 175
|
||||
#define PLIC_IE_WORDS (PLIC_SOURCES / 32 + 1)
|
||||
|
||||
static u8 plic_priority[PLIC_SOURCES];
|
||||
static u8 plic_priority[1 + PLIC_SOURCES];
|
||||
static u32 plic_sie[PLIC_IE_WORDS];
|
||||
static u32 plic_threshold;
|
||||
|
||||
@@ -202,6 +205,24 @@ static int sun20i_d1_final_init(bool cold_boot, const struct fdt_match *match)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sbi_cpu_idle_state sun20i_d1_cpu_idle_states[] = {
|
||||
{
|
||||
.name = "cpu-nonretentive",
|
||||
.suspend_param = SBI_HSM_SUSPEND_NON_RET_DEFAULT,
|
||||
.local_timer_stop = true,
|
||||
.entry_latency_us = 40,
|
||||
.exit_latency_us = 67,
|
||||
.min_residency_us = 1100,
|
||||
.wakeup_latency_us = 67,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int sun20i_d1_fdt_fixup(void *fdt, const struct fdt_match *match)
|
||||
{
|
||||
return fdt_add_cpu_idle_states(fdt, sun20i_d1_cpu_idle_states);
|
||||
}
|
||||
|
||||
static void thead_c9xx_pmu_ctr_enable_irq(uint32_t ctr_idx)
|
||||
{
|
||||
unsigned long mip_val;
|
||||
@@ -218,9 +239,17 @@ static void thead_c9xx_pmu_ctr_enable_irq(uint32_t ctr_idx)
|
||||
* Otherwise, there will be race conditions where we may clear the bit
|
||||
* the software is yet to handle the interrupt.
|
||||
*/
|
||||
if (!(mip_val & THEAD_C9XX_MIP_MOIP))
|
||||
if (mip_val & THEAD_C9XX_MIP_MOIP)
|
||||
csr_clear(THEAD_C9XX_CSR_MCOUNTEROF, BIT(ctr_idx));
|
||||
|
||||
/**
|
||||
* This register is described in c9xx document as the control register
|
||||
* for enabling writes to the superuser state counter. However, if the
|
||||
* corresponding bit is not set to 1, scounterof will always read as 0
|
||||
* when the counter register overflows.
|
||||
*/
|
||||
csr_set(THEAD_C9XX_CSR_MCOUNTERWEN, BIT(ctr_idx));
|
||||
|
||||
/**
|
||||
* SSCOFPMF uses the OF bit for enabling/disabling the interrupt,
|
||||
* while the C9XX has designated enable bits.
|
||||
@@ -231,6 +260,16 @@ static void thead_c9xx_pmu_ctr_enable_irq(uint32_t ctr_idx)
|
||||
|
||||
static void thead_c9xx_pmu_ctr_disable_irq(uint32_t ctr_idx)
|
||||
{
|
||||
unsigned long mip_val;
|
||||
|
||||
if (ctr_idx >= SBI_PMU_HW_CTR_MAX)
|
||||
return;
|
||||
|
||||
mip_val = csr_read(CSR_MIP);
|
||||
if (mip_val & THEAD_C9XX_MIP_MOIP)
|
||||
csr_clear(THEAD_C9XX_CSR_MCOUNTEROF, BIT(ctr_idx));
|
||||
|
||||
csr_clear(THEAD_C9XX_CSR_MCOUNTERWEN, BIT(ctr_idx));
|
||||
csr_clear(THEAD_C9XX_CSR_MCOUNTERINTEN, BIT(ctr_idx));
|
||||
}
|
||||
|
||||
@@ -265,5 +304,6 @@ static const struct fdt_match sun20i_d1_match[] = {
|
||||
const struct platform_override sun20i_d1 = {
|
||||
.match_table = sun20i_d1_match,
|
||||
.final_init = sun20i_d1_final_init,
|
||||
.fdt_fixup = sun20i_d1_fdt_fixup,
|
||||
.extensions_init = sun20i_d1_extensions_init,
|
||||
};
|
||||
|
||||
5
platform/generic/andes/Kconfig
Normal file
5
platform/generic/andes/Kconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
config ANDES45_PMA
|
||||
bool "Andes PMA support"
|
||||
default n
|
||||
@@ -10,6 +10,105 @@
|
||||
#include <platform_override.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/fdt/fdt_fixup.h>
|
||||
#include <sbi_utils/sys/atcsmu.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_init.h>
|
||||
#include <andes/andes45.h>
|
||||
|
||||
static struct smu_data smu = { 0 };
|
||||
extern void __ae350_enable_coherency_warmboot(void);
|
||||
extern void __ae350_disable_coherency(void);
|
||||
|
||||
static __always_inline bool is_andes25(void)
|
||||
{
|
||||
ulong marchid = csr_read(CSR_MARCHID);
|
||||
return !!(EXTRACT_FIELD(marchid, CSR_MARCHID_MICROID) == 0xa25);
|
||||
}
|
||||
|
||||
static int ae350_hart_start(u32 hartid, ulong saddr)
|
||||
{
|
||||
/* Don't send wakeup command at boot-time */
|
||||
if (!sbi_init_count(hartid) || (is_andes25() && hartid == 0))
|
||||
return sbi_ipi_raw_send(hartid);
|
||||
|
||||
/* Write wakeup command to the sleep hart */
|
||||
smu_set_command(&smu, WAKEUP_CMD, hartid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ae350_hart_stop(void)
|
||||
{
|
||||
int rc;
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
/**
|
||||
* For Andes AX25MP, the hart0 shares power domain with
|
||||
* L2-cache, instead of turning it off, it should fall
|
||||
* through and jump to warmboot_addr.
|
||||
*/
|
||||
if (is_andes25() && hartid == 0)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
if (!smu_support_sleep_mode(&smu, DEEPSLEEP_MODE, hartid))
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/**
|
||||
* disable all events, the current hart will be
|
||||
* woken up from reset vector when other hart
|
||||
* writes its PCS (power control slot) control
|
||||
* register
|
||||
*/
|
||||
smu_set_wakeup_events(&smu, 0x0, hartid);
|
||||
smu_set_command(&smu, DEEP_SLEEP_CMD, hartid);
|
||||
|
||||
rc = smu_set_reset_vector(&smu, (ulong)__ae350_enable_coherency_warmboot,
|
||||
hartid);
|
||||
if (rc)
|
||||
goto fail;
|
||||
|
||||
__ae350_disable_coherency();
|
||||
|
||||
wfi();
|
||||
|
||||
fail:
|
||||
/* It should never reach here */
|
||||
sbi_hart_hang();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sbi_hsm_device andes_smu = {
|
||||
.name = "andes_smu",
|
||||
.hart_start = ae350_hart_start,
|
||||
.hart_stop = ae350_hart_stop,
|
||||
};
|
||||
|
||||
static void ae350_hsm_device_init(void)
|
||||
{
|
||||
int rc;
|
||||
void *fdt;
|
||||
|
||||
fdt = fdt_get_address();
|
||||
|
||||
rc = fdt_parse_compat_addr(fdt, (uint64_t *)&smu.addr,
|
||||
"andestech,atcsmu");
|
||||
|
||||
if (!rc) {
|
||||
sbi_hsm_set_device(&andes_smu);
|
||||
}
|
||||
}
|
||||
|
||||
static int ae350_final_init(bool cold_boot, const struct fdt_match *match)
|
||||
{
|
||||
if (cold_boot)
|
||||
ae350_hsm_device_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match andes_ae350_match[] = {
|
||||
{ .compatible = "andestech,ae350" },
|
||||
@@ -18,4 +117,5 @@ static const struct fdt_match andes_ae350_match[] = {
|
||||
|
||||
const struct platform_override andes_ae350 = {
|
||||
.match_table = andes_ae350_match,
|
||||
.final_init = ae350_final_init,
|
||||
};
|
||||
|
||||
350
platform/generic/andes/andes45-pma.c
Normal file
350
platform/generic/andes/andes45-pma.c
Normal file
@@ -0,0 +1,350 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Renesas Electronics Corp.
|
||||
*
|
||||
* Copyright (c) 2020 Andes Technology Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Nick Hu <nickhu@andestech.com>
|
||||
* Nylon Chen <nylon7@andestech.com>
|
||||
* Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
|
||||
*/
|
||||
|
||||
#include <andes/andes45_pma.h>
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
/* Configuration Registers */
|
||||
#define ANDES45_CSR_MMSC_CFG 0xFC2
|
||||
#define ANDES45_CSR_MMSC_PPMA_OFFSET (1 << 30)
|
||||
|
||||
#define ANDES45_PMAADDR_0 0xBD0
|
||||
|
||||
#define ANDES45_PMACFG_0 0xBC0
|
||||
|
||||
static inline unsigned long andes45_pma_read_cfg(unsigned int pma_cfg_off)
|
||||
{
|
||||
#define switchcase_pma_cfg_read(__pma_cfg_off, __val) \
|
||||
case __pma_cfg_off: \
|
||||
__val = csr_read(__pma_cfg_off); \
|
||||
break;
|
||||
#define switchcase_pma_cfg_read_2(__pma_cfg_off, __val) \
|
||||
switchcase_pma_cfg_read(__pma_cfg_off + 0, __val) \
|
||||
switchcase_pma_cfg_read(__pma_cfg_off + 2, __val)
|
||||
|
||||
unsigned long ret = 0;
|
||||
|
||||
switch (pma_cfg_off) {
|
||||
switchcase_pma_cfg_read_2(ANDES45_PMACFG_0, ret)
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown PMA CFG offset %#x", __func__, pma_cfg_off);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
#undef switchcase_pma_cfg_read_2
|
||||
#undef switchcase_pma_cfg_read
|
||||
}
|
||||
|
||||
static inline void andes45_pma_write_cfg(unsigned int pma_cfg_off, unsigned long val)
|
||||
{
|
||||
#define switchcase_pma_cfg_write(__pma_cfg_off, __val) \
|
||||
case __pma_cfg_off: \
|
||||
csr_write(__pma_cfg_off, __val); \
|
||||
break;
|
||||
#define switchcase_pma_cfg_write_2(__pma_cfg_off, __val) \
|
||||
switchcase_pma_cfg_write(__pma_cfg_off + 0, __val) \
|
||||
switchcase_pma_cfg_write(__pma_cfg_off + 2, __val)
|
||||
|
||||
switch (pma_cfg_off) {
|
||||
switchcase_pma_cfg_write_2(ANDES45_PMACFG_0, val)
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown PMA CFG offset %#x", __func__, pma_cfg_off);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef switchcase_pma_cfg_write_2
|
||||
#undef switchcase_pma_cfg_write
|
||||
}
|
||||
|
||||
static inline void andes45_pma_write_addr(unsigned int pma_addr_off, unsigned long val)
|
||||
{
|
||||
#define switchcase_pma_write(__pma_addr_off, __val) \
|
||||
case __pma_addr_off: \
|
||||
csr_write(__pma_addr_off, __val); \
|
||||
break;
|
||||
#define switchcase_pma_write_2(__pma_addr_off, __val) \
|
||||
switchcase_pma_write(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_write(__pma_addr_off + 1, __val)
|
||||
#define switchcase_pma_write_4(__pma_addr_off, __val) \
|
||||
switchcase_pma_write_2(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_write_2(__pma_addr_off + 2, __val)
|
||||
#define switchcase_pma_write_8(__pma_addr_off, __val) \
|
||||
switchcase_pma_write_4(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_write_4(__pma_addr_off + 4, __val)
|
||||
#define switchcase_pma_write_16(__pma_addr_off, __val) \
|
||||
switchcase_pma_write_8(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_write_8(__pma_addr_off + 8, __val)
|
||||
|
||||
switch (pma_addr_off) {
|
||||
switchcase_pma_write_16(ANDES45_PMAADDR_0, val)
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown PMA ADDR offset %#x", __func__, pma_addr_off);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef switchcase_pma_write_16
|
||||
#undef switchcase_pma_write_8
|
||||
#undef switchcase_pma_write_4
|
||||
#undef switchcase_pma_write_2
|
||||
#undef switchcase_pma_write
|
||||
}
|
||||
|
||||
static inline unsigned long andes45_pma_read_addr(unsigned int pma_addr_off)
|
||||
{
|
||||
#define switchcase_pma_read(__pma_addr_off, __val) \
|
||||
case __pma_addr_off: \
|
||||
__val = csr_read(__pma_addr_off); \
|
||||
break;
|
||||
#define switchcase_pma_read_2(__pma_addr_off, __val) \
|
||||
switchcase_pma_read(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_read(__pma_addr_off + 1, __val)
|
||||
#define switchcase_pma_read_4(__pma_addr_off, __val) \
|
||||
switchcase_pma_read_2(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_read_2(__pma_addr_off + 2, __val)
|
||||
#define switchcase_pma_read_8(__pma_addr_off, __val) \
|
||||
switchcase_pma_read_4(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_read_4(__pma_addr_off + 4, __val)
|
||||
#define switchcase_pma_read_16(__pma_addr_off, __val) \
|
||||
switchcase_pma_read_8(__pma_addr_off + 0, __val) \
|
||||
switchcase_pma_read_8(__pma_addr_off + 8, __val)
|
||||
|
||||
unsigned long ret = 0;
|
||||
|
||||
switch (pma_addr_off) {
|
||||
switchcase_pma_read_16(ANDES45_PMAADDR_0, ret)
|
||||
|
||||
default:
|
||||
sbi_panic("%s: Unknown PMA ADDR offset %#x", __func__, pma_addr_off);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
#undef switchcase_pma_read_16
|
||||
#undef switchcase_pma_read_8
|
||||
#undef switchcase_pma_read_4
|
||||
#undef switchcase_pma_read_2
|
||||
#undef switchcase_pma_read
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
andes45_pma_setup(const struct andes45_pma_region *pma_region,
|
||||
unsigned int entry_id)
|
||||
{
|
||||
unsigned long size = pma_region->size;
|
||||
unsigned long addr = pma_region->pa;
|
||||
unsigned int pma_cfg_addr;
|
||||
unsigned long pmacfg_val;
|
||||
unsigned long pmaaddr;
|
||||
char *pmaxcfg;
|
||||
|
||||
/* Check for 4KiB granularity */
|
||||
if (size < (1 << 12))
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Check size is power of 2 */
|
||||
if (size & (size - 1))
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (entry_id > 15)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (!(pma_region->flags & ANDES45_PMACFG_ETYP_NAPOT))
|
||||
return SBI_EINVAL;
|
||||
|
||||
if ((addr & (size - 1)) != 0)
|
||||
return SBI_EINVAL;
|
||||
|
||||
pma_cfg_addr = entry_id / 8 ? ANDES45_PMACFG_0 + 2 : ANDES45_PMACFG_0;
|
||||
pmacfg_val = andes45_pma_read_cfg(pma_cfg_addr);
|
||||
pmaxcfg = (char *)&pmacfg_val + (entry_id % 8);
|
||||
*pmaxcfg = 0;
|
||||
*pmaxcfg = pma_region->flags;
|
||||
|
||||
andes45_pma_write_cfg(pma_cfg_addr, pmacfg_val);
|
||||
|
||||
pmaaddr = (addr >> 2) + (size >> 3) - 1;
|
||||
|
||||
andes45_pma_write_addr(ANDES45_PMAADDR_0 + entry_id, pmaaddr);
|
||||
|
||||
return andes45_pma_read_addr(ANDES45_PMAADDR_0 + entry_id) == pmaaddr ?
|
||||
pmaaddr : SBI_EINVAL;
|
||||
}
|
||||
|
||||
static int andes45_fdt_pma_resv(void *fdt, const struct andes45_pma_region *pma,
|
||||
unsigned int index, int parent)
|
||||
{
|
||||
int na = fdt_address_cells(fdt, 0);
|
||||
int ns = fdt_size_cells(fdt, 0);
|
||||
static bool dma_default = false;
|
||||
fdt32_t addr_high, addr_low;
|
||||
fdt32_t size_high, size_low;
|
||||
int subnode, err;
|
||||
fdt32_t reg[4];
|
||||
fdt32_t *val;
|
||||
char name[32];
|
||||
|
||||
addr_high = (u64)pma->pa >> 32;
|
||||
addr_low = pma->pa;
|
||||
size_high = (u64)pma->size >> 32;
|
||||
size_low = pma->size;
|
||||
|
||||
if (na > 1 && addr_high)
|
||||
sbi_snprintf(name, sizeof(name),
|
||||
"pma_resv%d@%x,%x", index,
|
||||
addr_high, addr_low);
|
||||
else
|
||||
sbi_snprintf(name, sizeof(name),
|
||||
"pma_resv%d@%x", index,
|
||||
addr_low);
|
||||
|
||||
subnode = fdt_add_subnode(fdt, parent, name);
|
||||
if (subnode < 0)
|
||||
return subnode;
|
||||
|
||||
if (pma->shared_dma) {
|
||||
err = fdt_setprop_string(fdt, subnode, "compatible", "shared-dma-pool");
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pma->no_map) {
|
||||
err = fdt_setprop_empty(fdt, subnode, "no-map");
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Linux allows single linux,dma-default region. */
|
||||
if (pma->dma_default) {
|
||||
if (dma_default)
|
||||
return SBI_EINVAL;
|
||||
|
||||
err = fdt_setprop_empty(fdt, subnode, "linux,dma-default");
|
||||
if (err < 0)
|
||||
return err;
|
||||
dma_default = true;
|
||||
}
|
||||
|
||||
/* encode the <reg> property value */
|
||||
val = reg;
|
||||
if (na > 1)
|
||||
*val++ = cpu_to_fdt32(addr_high);
|
||||
*val++ = cpu_to_fdt32(addr_low);
|
||||
if (ns > 1)
|
||||
*val++ = cpu_to_fdt32(size_high);
|
||||
*val++ = cpu_to_fdt32(size_low);
|
||||
|
||||
err = fdt_setprop(fdt, subnode, "reg", reg,
|
||||
(na + ns) * sizeof(fdt32_t));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int andes45_fdt_reserved_memory_fixup(void *fdt,
|
||||
const struct andes45_pma_region *pma,
|
||||
unsigned int entry)
|
||||
{
|
||||
int parent;
|
||||
|
||||
/* try to locate the reserved memory node */
|
||||
parent = fdt_path_offset(fdt, "/reserved-memory");
|
||||
if (parent < 0) {
|
||||
int na = fdt_address_cells(fdt, 0);
|
||||
int ns = fdt_size_cells(fdt, 0);
|
||||
int err;
|
||||
|
||||
/* if such node does not exist, create one */
|
||||
parent = fdt_add_subnode(fdt, 0, "reserved-memory");
|
||||
if (parent < 0)
|
||||
return parent;
|
||||
|
||||
err = fdt_setprop_empty(fdt, parent, "ranges");
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = fdt_setprop_u32(fdt, parent, "#size-cells", ns);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = fdt_setprop_u32(fdt, parent, "#address-cells", na);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return andes45_fdt_pma_resv(fdt, pma, entry, parent);
|
||||
}
|
||||
|
||||
int andes45_pma_setup_regions(const struct andes45_pma_region *pma_regions,
|
||||
unsigned int pma_regions_count)
|
||||
{
|
||||
unsigned long mmsc = csr_read(ANDES45_CSR_MMSC_CFG);
|
||||
unsigned int dt_populate_cnt;
|
||||
unsigned int i, j;
|
||||
unsigned long pa;
|
||||
void *fdt;
|
||||
int ret;
|
||||
|
||||
if (!pma_regions || !pma_regions_count)
|
||||
return 0;
|
||||
|
||||
if (pma_regions_count > ANDES45_MAX_PMA_REGIONS)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if ((mmsc & ANDES45_CSR_MMSC_PPMA_OFFSET) == 0)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/* Configure the PMA regions */
|
||||
for (i = 0; i < pma_regions_count; i++) {
|
||||
pa = andes45_pma_setup(&pma_regions[i], i);
|
||||
if (pa == SBI_EINVAL)
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
dt_populate_cnt = 0;
|
||||
for (i = 0; i < pma_regions_count; i++) {
|
||||
if (!pma_regions[i].dt_populate)
|
||||
continue;
|
||||
dt_populate_cnt++;
|
||||
}
|
||||
|
||||
if (!dt_populate_cnt)
|
||||
return 0;
|
||||
|
||||
fdt = fdt_get_address();
|
||||
|
||||
ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + (64 * dt_populate_cnt));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0, j = 0; i < pma_regions_count; i++) {
|
||||
if (!pma_regions[i].dt_populate)
|
||||
continue;
|
||||
|
||||
ret = andes45_fdt_reserved_memory_fixup(fdt, &pma_regions[i], j++);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3,4 +3,6 @@
|
||||
#
|
||||
|
||||
carray-platform_override_modules-$(CONFIG_PLATFORM_ANDES_AE350) += andes_ae350
|
||||
platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o
|
||||
platform-objs-$(CONFIG_PLATFORM_ANDES_AE350) += andes/ae350.o andes/sleep.o
|
||||
|
||||
platform-objs-$(CONFIG_ANDES45_PMA) += andes/andes45-pma.o
|
||||
|
||||
70
platform/generic/andes/sleep.S
Normal file
70
platform/generic/andes/sleep.S
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2023 Andes Technology Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yu Chien Peter Lin <peterlin@andestech.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <andes/andes45.h>
|
||||
|
||||
.section .text, "ax", %progbits
|
||||
.align 3
|
||||
.global __ae350_disable_coherency
|
||||
__ae350_disable_coherency:
|
||||
/* flush d-cache */
|
||||
csrw CSR_MCCTLCOMMAND, 0x6
|
||||
/* disable i/d-cache */
|
||||
csrc CSR_MCACHE_CTL, 0x3
|
||||
/* disable d-cache coherency */
|
||||
lui t1, 0x80
|
||||
csrc CSR_MCACHE_CTL, t1
|
||||
/*
|
||||
* wait for mcache_ctl.DC_COHSTA to be cleared,
|
||||
* the bit is hard-wired 0 on platforms w/o CM
|
||||
* (Coherence Manager)
|
||||
*/
|
||||
check_cm_disabled:
|
||||
csrr t1, CSR_MCACHE_CTL
|
||||
srli t1, t1, 20
|
||||
andi t1, t1, 0x1
|
||||
bnez t1, check_cm_disabled
|
||||
|
||||
ret
|
||||
|
||||
.section .text, "ax", %progbits
|
||||
.align 3
|
||||
.global __ae350_enable_coherency
|
||||
__ae350_enable_coherency:
|
||||
/* enable d-cache coherency */
|
||||
lui t1, 0x80
|
||||
csrs CSR_MCACHE_CTL, t1
|
||||
/*
|
||||
* mcache_ctl.DC_COHEN is hard-wired 0 on platforms
|
||||
* w/o CM support
|
||||
*/
|
||||
csrr t1, CSR_MCACHE_CTL
|
||||
srli t1, t1, 19
|
||||
andi t1, t1, 0x1
|
||||
beqz t1, enable_L1_cache
|
||||
/* wait for mcache_ctl.DC_COHSTA to be set */
|
||||
check_cm_enabled:
|
||||
csrr t1, CSR_MCACHE_CTL
|
||||
srli t1, t1, 20
|
||||
andi t1, t1, 0x1
|
||||
beqz t1, check_cm_enabled
|
||||
enable_L1_cache:
|
||||
/* enable i/d-cache */
|
||||
csrs CSR_MCACHE_CTL, 0x3
|
||||
|
||||
ret
|
||||
|
||||
.section .text, "ax", %progbits
|
||||
.align 3
|
||||
.global __ae350_enable_coherency_warmboot
|
||||
__ae350_enable_coherency_warmboot:
|
||||
call ra, __ae350_enable_coherency
|
||||
j _start_warm
|
||||
@@ -3,10 +3,15 @@ CONFIG_PLATFORM_ANDES_AE350=y
|
||||
CONFIG_PLATFORM_RENESAS_RZFIVE=y
|
||||
CONFIG_PLATFORM_SIFIVE_FU540=y
|
||||
CONFIG_PLATFORM_SIFIVE_FU740=y
|
||||
CONFIG_PLATFORM_STARFIVE_JH7110=y
|
||||
CONFIG_PLATFORM_SOPHGO_MANGO=y
|
||||
CONFIG_FDT_GPIO=y
|
||||
CONFIG_FDT_GPIO_SIFIVE=y
|
||||
CONFIG_FDT_GPIO_SOPHGO=y
|
||||
CONFIG_FDT_GPIO_STARFIVE=y
|
||||
CONFIG_FDT_I2C=y
|
||||
CONFIG_FDT_I2C_SIFIVE=y
|
||||
CONFIG_FDT_I2C_DW=y
|
||||
CONFIG_FDT_IPI=y
|
||||
CONFIG_FDT_IPI_MSWI=y
|
||||
CONFIG_FDT_IPI_PLICSW=y
|
||||
@@ -19,6 +24,9 @@ CONFIG_FDT_RESET_ATCWDT200=y
|
||||
CONFIG_FDT_RESET_GPIO=y
|
||||
CONFIG_FDT_RESET_HTIF=y
|
||||
CONFIG_FDT_RESET_SIFIVE_TEST=y
|
||||
CONFIG_FDT_RESET_SOPHGO_CPLD=y
|
||||
CONFIG_FDT_RESET_SOPHGO_MCU=y
|
||||
CONFIG_FDT_RESET_SOPHGO_WDT=y
|
||||
CONFIG_FDT_RESET_SUNXI_WDT=y
|
||||
CONFIG_FDT_RESET_THEAD=y
|
||||
CONFIG_FDT_SERIAL=y
|
||||
@@ -34,4 +42,5 @@ CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
|
||||
CONFIG_FDT_TIMER=y
|
||||
CONFIG_FDT_TIMER_MTIMER=y
|
||||
CONFIG_FDT_TIMER_PLMT=y
|
||||
CONFIG_FDT_TIMER_SG2042_GMT=y
|
||||
CONFIG_SERIAL_SEMIHOSTING=y
|
||||
|
||||
10
platform/generic/include/andes/andes45.h
Normal file
10
platform/generic/include/andes/andes45.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _RISCV_ANDES45_H
|
||||
#define _RISCV_ANDES45_H
|
||||
|
||||
#define CSR_MARCHID_MICROID 0xfff
|
||||
|
||||
/* Memory and Miscellaneous Registers */
|
||||
#define CSR_MCACHE_CTL 0x7ca
|
||||
#define CSR_MCCTLCOMMAND 0x7cc
|
||||
|
||||
#endif /* _RISCV_ANDES45_H */
|
||||
48
platform/generic/include/andes/andes45_pma.h
Normal file
48
platform/generic/include/andes/andes45_pma.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Renesas Electronics Corp.
|
||||
*/
|
||||
|
||||
#ifndef _ANDES45_PMA_H_
|
||||
#define _ANDES45_PMA_H_
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define ANDES45_MAX_PMA_REGIONS 16
|
||||
|
||||
/* Naturally aligned power of 2 region */
|
||||
#define ANDES45_PMACFG_ETYP_NAPOT 3
|
||||
|
||||
/* Memory, Non-cacheable, Bufferable */
|
||||
#define ANDES45_PMACFG_MTYP_MEM_NON_CACHE_BUF (3 << 2)
|
||||
|
||||
/**
|
||||
* struct andes45_pma_region - Describes PMA regions
|
||||
*
|
||||
* @pa: Address to be configured in the PMA
|
||||
* @size: Size of the region
|
||||
* @flags: Flags to be set for the PMA region
|
||||
* @dt_populate: Boolean flag indicating if the DT entry should be
|
||||
* populated for the given PMA region
|
||||
* @shared_dma: Boolean flag if set "shared-dma-pool" property will
|
||||
* be set in the DT node
|
||||
* @no_map: Boolean flag if set "no-map" property will be set in the
|
||||
* DT node
|
||||
* @dma_default: Boolean flag if set "linux,dma-default" property will
|
||||
* be set in the DT node. Note Linux expects single node
|
||||
* with this property set.
|
||||
*/
|
||||
struct andes45_pma_region {
|
||||
unsigned long pa;
|
||||
unsigned long size;
|
||||
u8 flags:7;
|
||||
bool dt_populate;
|
||||
bool shared_dma;
|
||||
bool no_map;
|
||||
bool dma_default;
|
||||
};
|
||||
|
||||
int andes45_pma_setup_regions(const struct andes45_pma_region *pma_regions,
|
||||
unsigned int pma_regions_count);
|
||||
|
||||
#endif /* _ANDES45_PMA_H_ */
|
||||
@@ -18,6 +18,8 @@ struct platform_override {
|
||||
const struct fdt_match *match_table;
|
||||
u64 (*features)(const struct fdt_match *match);
|
||||
u64 (*tlbr_flush_limit)(const struct fdt_match *match);
|
||||
bool (*cold_boot_allowed)(u32 hartid, const struct fdt_match *match);
|
||||
bool (*force_emulate_time_csr)(const struct fdt_match *match);
|
||||
int (*early_init)(bool cold_boot, const struct fdt_match *match);
|
||||
int (*final_init)(bool cold_boot, const struct fdt_match *match);
|
||||
void (*early_exit)(const struct fdt_match *match);
|
||||
@@ -25,8 +27,8 @@ struct platform_override {
|
||||
int (*fdt_fixup)(void *fdt, const struct fdt_match *match);
|
||||
int (*extensions_init)(const struct fdt_match *match,
|
||||
struct sbi_hart_features *hfeatures);
|
||||
int (*vendor_ext_check)(long extid, const struct fdt_match *match);
|
||||
int (*vendor_ext_provider)(long extid, long funcid,
|
||||
void (*fw_init)(void *fdt, const struct fdt_match *match);
|
||||
int (*vendor_ext_provider)(long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_value,
|
||||
struct sbi_trap_info *out_trap,
|
||||
|
||||
@@ -124,4 +124,6 @@
|
||||
#define THEAD_C9XX_IRQ_PMU_OVF 17
|
||||
#define THEAD_C9XX_MIP_MOIP (_UL(1) << THEAD_C9XX_IRQ_PMU_OVF)
|
||||
|
||||
extern const struct sbi_pmu_device thead_c9xx_pmu_device;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_string.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi_utils/fdt/fdt_domain.h>
|
||||
#include <sbi_utils/fdt/fdt_fixup.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
@@ -85,6 +86,9 @@ unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,
|
||||
|
||||
fw_platform_lookup_special(fdt, root_offset);
|
||||
|
||||
if (generic_plat && generic_plat->fw_init)
|
||||
generic_plat->fw_init(fdt, generic_plat_match);
|
||||
|
||||
model = fdt_getprop(fdt, root_offset, "model", &len);
|
||||
if (model)
|
||||
sbi_strncpy(platform.name, model, sizeof(platform.name) - 1);
|
||||
@@ -122,6 +126,22 @@ fail:
|
||||
wfi();
|
||||
}
|
||||
|
||||
static bool generic_cold_boot_allowed(u32 hartid)
|
||||
{
|
||||
if (generic_plat && generic_plat->cold_boot_allowed)
|
||||
return generic_plat->cold_boot_allowed(
|
||||
hartid, generic_plat_match);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generic_force_emulate_time_csr(void)
|
||||
{
|
||||
if (generic_plat && generic_plat->force_emulate_time_csr)
|
||||
return generic_plat->force_emulate_time_csr(generic_plat_match);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int generic_nascent_init(void)
|
||||
{
|
||||
if (platform_has_mlevel_imsic)
|
||||
@@ -169,27 +189,20 @@ static int generic_final_init(bool cold_boot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_vendor_ext_check(long extid)
|
||||
static bool generic_vendor_ext_check(void)
|
||||
{
|
||||
if (generic_plat && generic_plat->vendor_ext_check)
|
||||
return generic_plat->vendor_ext_check(extid,
|
||||
generic_plat_match);
|
||||
|
||||
return 0;
|
||||
return (generic_plat && generic_plat->vendor_ext_provider) ?
|
||||
true : false;
|
||||
}
|
||||
|
||||
static int generic_vendor_ext_provider(long extid, long funcid,
|
||||
static int generic_vendor_ext_provider(long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_value,
|
||||
struct sbi_trap_info *out_trap)
|
||||
{
|
||||
if (generic_plat && generic_plat->vendor_ext_provider) {
|
||||
return generic_plat->vendor_ext_provider(extid, funcid, regs,
|
||||
out_value, out_trap,
|
||||
generic_plat_match);
|
||||
}
|
||||
|
||||
return SBI_ENOTSUPP;
|
||||
return generic_plat->vendor_ext_provider(funcid, regs,
|
||||
out_value, out_trap,
|
||||
generic_plat_match);
|
||||
}
|
||||
|
||||
static void generic_early_exit(void)
|
||||
@@ -215,7 +228,24 @@ static int generic_extensions_init(struct sbi_hart_features *hfeatures)
|
||||
|
||||
static int generic_domains_init(void)
|
||||
{
|
||||
return fdt_domains_populate(fdt_get_address());
|
||||
void *fdt = fdt_get_address();
|
||||
int offset, ret;
|
||||
|
||||
ret = fdt_domains_populate(fdt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset = fdt_path_offset(fdt, "/chosen");
|
||||
|
||||
if (offset >= 0) {
|
||||
offset = fdt_node_offset_by_compatible(fdt, offset,
|
||||
"opensbi,domain,config");
|
||||
if (offset >= 0 &&
|
||||
fdt_get_property(fdt, offset, "system-suspend-test", NULL))
|
||||
sbi_system_suspend_test_enable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 generic_tlbr_flush_limit(void)
|
||||
@@ -261,6 +291,8 @@ static int generic_console_init(void)
|
||||
}
|
||||
|
||||
const struct sbi_platform_operations platform_ops = {
|
||||
.cold_boot_allowed = generic_cold_boot_allowed,
|
||||
.force_emulate_time_csr = generic_force_emulate_time_csr,
|
||||
.nascent_init = generic_nascent_init,
|
||||
.early_init = generic_early_init,
|
||||
.final_init = generic_final_init,
|
||||
|
||||
@@ -4,9 +4,48 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <andes/andes45_pma.h>
|
||||
#include <platform_override.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
|
||||
static const struct andes45_pma_region renesas_rzfive_pma_regions[] = {
|
||||
{
|
||||
.pa = 0x58000000,
|
||||
.size = 0x8000000,
|
||||
.flags = ANDES45_PMACFG_ETYP_NAPOT |
|
||||
ANDES45_PMACFG_MTYP_MEM_NON_CACHE_BUF,
|
||||
.dt_populate = true,
|
||||
.shared_dma = true,
|
||||
.no_map = true,
|
||||
.dma_default = true,
|
||||
},
|
||||
};
|
||||
|
||||
static int renesas_rzfive_final_init(bool cold_boot, const struct fdt_match *match)
|
||||
{
|
||||
return andes45_pma_setup_regions(renesas_rzfive_pma_regions,
|
||||
array_size(renesas_rzfive_pma_regions));
|
||||
}
|
||||
|
||||
int renesas_rzfive_early_init(bool cold_boot, const struct fdt_match *match)
|
||||
{
|
||||
/*
|
||||
* Renesas RZ/Five RISC-V SoC has Instruction local memory and
|
||||
* Data local memory (ILM & DLM) mapped between region 0x30000
|
||||
* to 0x4FFFF. When a virtual address falls within this range,
|
||||
* the MMU doesn't trigger a page fault; it assumes the virtual
|
||||
* address is a physical address which can cause undesired
|
||||
* behaviours for statically linked applications/libraries. To
|
||||
* avoid this, add the ILM/DLM memory regions to the root domain
|
||||
* region of the PMPU with permissions set to 0x0 for S/U modes
|
||||
* so that any access to these regions gets blocked and for M-mode
|
||||
* we grant full access.
|
||||
*/
|
||||
return sbi_domain_root_add_memrange(0x30000, 0x20000, 0x1000,
|
||||
SBI_DOMAIN_MEMREGION_M_RWX);
|
||||
}
|
||||
|
||||
static const struct fdt_match renesas_rzfive_match[] = {
|
||||
{ .compatible = "renesas,r9a07g043f01" },
|
||||
{ /* sentinel */ }
|
||||
@@ -14,4 +53,6 @@ static const struct fdt_match renesas_rzfive_match[] = {
|
||||
|
||||
const struct platform_override renesas_rzfive = {
|
||||
.match_table = renesas_rzfive_match,
|
||||
.early_init = renesas_rzfive_early_init,
|
||||
.final_init = renesas_rzfive_final_init,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user