mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-08-24 15:31:22 +01:00
Compare commits
102 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4489876e93 | ||
![]() |
3f66465fb6 | ||
![]() |
c6fdbcf83f | ||
![]() |
6f1fe98c2f | ||
![]() |
d76a196bfc | ||
![]() |
7738345396 | ||
![]() |
c6530012d4 | ||
![]() |
a07402ac9c | ||
![]() |
187127fb89 | ||
![]() |
551c70c040 | ||
![]() |
9dc5ec5c51 | ||
![]() |
5e5675874c | ||
![]() |
69be3dff9d | ||
![]() |
2b79b694a8 | ||
![]() |
415ecf28f7 | ||
![]() |
8c362e7d06 | ||
![]() |
2ea7799d56 | ||
![]() |
79e42eb2d6 | ||
![]() |
b20ed9febe | ||
![]() |
ce1d6188a2 | ||
![]() |
adc3388d76 | ||
![]() |
cb8271c8e4 | ||
![]() |
ff65bfec4e | ||
![]() |
295e5f3c69 | ||
![]() |
fab0379bb6 | ||
![]() |
f067bb84cf | ||
![]() |
1bc67db80c | ||
![]() |
575bb4e8ca | ||
![]() |
616da52e18 | ||
![]() |
90a9dd2b22 | ||
![]() |
851c14d455 | ||
![]() |
9a7a677d5f | ||
![]() |
a3a3c60b66 | ||
![]() |
4eacd8229b | ||
![]() |
998ed43fde | ||
![]() |
4ee0c57969 | ||
![]() |
3a69d12fc3 | ||
![]() |
bfeb305e0f | ||
![]() |
1e62705adc | ||
![]() |
73cf511914 | ||
![]() |
7fb474b9dd | ||
![]() |
f726f2dc01 | ||
![]() |
023f0ad2d9 | ||
![]() |
994ace30f7 | ||
![]() |
be4903ae00 | ||
![]() |
cad6c91045 | ||
![]() |
a6ab94fdbf | ||
![]() |
97a17c2e5c | ||
![]() |
dbc3d8f0ef | ||
![]() |
d4b563c881 | ||
![]() |
5b8b377178 | ||
![]() |
5a6be99cc5 | ||
![]() |
1a754bb365 | ||
![]() |
b0c9df514b | ||
![]() |
e576b3e620 | ||
![]() |
474a9d4555 | ||
![]() |
d62f6da062 | ||
![]() |
4035ae94be | ||
![]() |
9cd95e13bb | ||
![]() |
c1e47d0c3f | ||
![]() |
5c5cbb53a4 | ||
![]() |
3383d6a4d1 | ||
![]() |
d44568a0f2 | ||
![]() |
499601a4ff | ||
![]() |
794986f87f | ||
![]() |
47d676570d | ||
![]() |
31fecad46d | ||
![]() |
722f80d8e9 | ||
![]() |
7924a0b220 | ||
![]() |
1b42d3ace3 | ||
![]() |
555bdb1cf3 | ||
![]() |
d552fc8d36 | ||
![]() |
b6b7220a47 | ||
![]() |
2dfbd3c0e2 | ||
![]() |
4998a712b2 | ||
![]() |
f3f4604c19 | ||
![]() |
f2ccf2f783 | ||
![]() |
3a69cc1487 | ||
![]() |
8e2ef4f7af | ||
![]() |
34612193af | ||
![]() |
99792653de | ||
![]() |
7127aaaaf7 | ||
![]() |
811da5c541 | ||
![]() |
9f73669959 | ||
![]() |
55e79f823d | ||
![]() |
10509405b2 | ||
![]() |
5f56314618 | ||
![]() |
222132f48c | ||
![]() |
65b4c7c01e | ||
![]() |
8f96070067 | ||
![]() |
01250d0044 | ||
![]() |
ce4c0188d9 | ||
![]() |
6ad8917b7e | ||
![]() |
5d53b55aa7 | ||
![]() |
a26dc609df | ||
![]() |
3b7c204dca | ||
![]() |
632f59392b | ||
![]() |
5d025eb235 | ||
![]() |
fb688d9e9d | ||
![]() |
8257262dbf | ||
![]() |
6dde43584f | ||
![]() |
5b9960379f |
26
Makefile
26
Makefile
@@ -153,6 +153,9 @@ OPENSBI_LD_PIE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) $(USE_LD_FLAG) -fP
|
||||
# Check whether the compiler supports -m(no-)save-restore
|
||||
CC_SUPPORT_SAVE_RESTORE := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -mno-save-restore -x c /dev/null -o /dev/null 2>&1 | grep "\-save\-restore" >/dev/null && echo n || echo y)
|
||||
|
||||
# Check whether the assembler and the compiler support the Zicsr and Zifencei extensions
|
||||
CC_SUPPORT_ZICSR_ZIFENCEI := $(shell $(CC) $(CLANG_TARGET) $(RELAX_FLAG) -nostdlib -march=rv$(OPENSBI_CC_XLEN)imafd_zicsr_zifencei -x c /dev/null -o /dev/null 2>&1 | grep "zicsr\|zifencei" > /dev/null && echo n || echo y)
|
||||
|
||||
# Build Info:
|
||||
# OPENSBI_BUILD_TIME_STAMP -- the compilation time stamp
|
||||
# OPENSBI_BUILD_COMPILER_VERSION -- the compiler version info
|
||||
@@ -223,7 +226,11 @@ ifndef PLATFORM_RISCV_ABI
|
||||
endif
|
||||
ifndef PLATFORM_RISCV_ISA
|
||||
ifneq ($(PLATFORM_RISCV_TOOLCHAIN_DEFAULT), 1)
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc
|
||||
ifeq ($(CC_SUPPORT_ZICSR_ZIFENCEI), y)
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc_zicsr_zifencei
|
||||
else
|
||||
PLATFORM_RISCV_ISA = rv$(PLATFORM_RISCV_XLEN)imafdc
|
||||
endif
|
||||
else
|
||||
PLATFORM_RISCV_ISA = $(OPENSBI_CC_ISA)
|
||||
endif
|
||||
@@ -397,6 +404,10 @@ compile_d2c = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
$(if $($(2)-varprefix-$(3)),$(eval D2C_NAME_PREFIX := $($(2)-varprefix-$(3))),$(eval D2C_NAME_PREFIX := $(5))) \
|
||||
$(if $($(2)-padding-$(3)),$(eval D2C_PADDING_BYTES := $($(2)-padding-$(3))),$(eval D2C_PADDING_BYTES := 0)) \
|
||||
$(src_dir)/scripts/d2c.sh -i $(6) -a $(D2C_ALIGN_BYTES) -p $(D2C_NAME_PREFIX) -t $(D2C_PADDING_BYTES) > $(1)
|
||||
compile_carray = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " CARRAY $(subst $(build_dir)/,,$(1))"; \
|
||||
$(eval CARRAY_VAR_LIST := $(carray-$(subst .c,,$(shell basename $(1)))-y)) \
|
||||
$(src_dir)/scripts/carray.sh -i $(2) -l "$(CARRAY_VAR_LIST)" > $(1)
|
||||
compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
|
||||
echo " GEN-DEP $(subst $(build_dir)/,,$(1))"; \
|
||||
echo "$(1:.dep=$(2)): $(3)" >> $(1)
|
||||
@@ -430,6 +441,9 @@ $(build_dir)/%.dep: $(src_dir)/%.c
|
||||
$(build_dir)/%.o: $(src_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(build_dir)/%.o: $(build_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
ifeq ($(BUILD_INFO),y)
|
||||
$(build_dir)/lib/sbi/sbi_init.o: $(libsbi_dir)/sbi_init.c FORCE
|
||||
$(call compile_cc,$@,$<)
|
||||
@@ -441,6 +455,13 @@ $(build_dir)/%.dep: $(src_dir)/%.S
|
||||
$(build_dir)/%.o: $(src_dir)/%.S
|
||||
$(call compile_as,$@,$<)
|
||||
|
||||
$(build_dir)/%.dep: $(src_dir)/%.carray
|
||||
$(call compile_gen_dep,$@,.c,$<)
|
||||
$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
|
||||
|
||||
$(build_dir)/%.c: $(src_dir)/%.carray
|
||||
$(call compile_carray,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
|
||||
$(call compile_objcopy,$@,$<)
|
||||
|
||||
@@ -456,9 +477,6 @@ $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
|
||||
$(platform_build_dir)/%.o: $(platform_src_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.o: $(platform_build_dir)/%.c
|
||||
$(call compile_cc,$@,$<)
|
||||
|
||||
$(platform_build_dir)/%.dep: $(platform_src_dir)/%.S
|
||||
$(call compile_as_dep,$@,$<)
|
||||
|
||||
|
@@ -52,12 +52,11 @@ DTS Example1: (Single core, eg: Allwinner D1 - c906)
|
||||
ranges;
|
||||
|
||||
clint0: clint@14000000 {
|
||||
compatible = "riscv,clint0";
|
||||
compatible = "allwinner,sun20i-d1-clint";
|
||||
interrupts-extended = <
|
||||
&cpu0_intc 3 &cpu0_intc 7
|
||||
>;
|
||||
reg = <0x0 0x14000000 0x0 0x04000000>;
|
||||
clint,has-no-64bit-mmio;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@10000000 {
|
||||
@@ -163,7 +162,6 @@ DTS Example2: (Multi cores with soc reset-regs)
|
||||
&cpu4_intc 3 &cpu4_intc 7
|
||||
>;
|
||||
reg = <0xff 0xdc000000 0x0 0x04000000>;
|
||||
clint,has-no-64bit-mmio;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@ffd8000000 {
|
||||
|
@@ -51,37 +51,50 @@ shouldn't encode any raw event.
|
||||
|
||||
* **riscv,raw-event-to-mhpmcounters**(Optional) - It represents an ONE-to-MANY
|
||||
or MANY-to-MANY mapping between the raw event(s) and all the MHPMCOUNTERx in
|
||||
a bitmap format that can be used to monitor that raw event, which depends on
|
||||
how the platform encodes the monitor events. Currently, only the following three
|
||||
encoding methods are supported, encoding each event as a number, using a bitmap
|
||||
to encode monitor events, and mixing the previous two methods. The information
|
||||
is encoded in a table format where each row represent the specific raw event(s).
|
||||
The first column represents a 64-bit selector value which can indicate an
|
||||
monitor event ID (encoded by a number) or an event set (encoded by a bitmap).
|
||||
In case of the latter, the lower bits used to encode a set of events should be
|
||||
set to zero. The second column is a 64-bit selector mask where any bits used
|
||||
for event encoding will be cleared. If a platform directly encodes each raw PMU
|
||||
event as a unique ID, the value of select_mask will be 0xffffffff_ffffffff.
|
||||
The third column represent a bitmap of all the MHPMCOUNTERx that can be used for
|
||||
monitoring the specified event(s).
|
||||
a bitmap format that can be used to monitor that raw event. The encoding of the
|
||||
raw events are platform specific. The information is encoded in a table format
|
||||
where each row represent the specific raw event(s). The first column is a 64bit
|
||||
match value where the invariant bits of range of events are set. The second
|
||||
column is a 64 bit mask that will have all the variant bits of the range of
|
||||
events cleared. Every other bits should be set in the mask.
|
||||
The third column is a 32bit value to represent bitmap of all MHPMCOUNTERx that
|
||||
can monitor these set of event(s).
|
||||
If a platform directly encodes each raw PMU event as a unique ID, the value of
|
||||
select_mask must be 0xffffffff_ffffffff.
|
||||
|
||||
*Note:* A platform may choose to provide the mapping between event & counters
|
||||
via platform hooks rather than the device tree.
|
||||
|
||||
### Example
|
||||
### Example 1
|
||||
|
||||
```
|
||||
pmu {
|
||||
compatible = "riscv,pmu";
|
||||
interrupts = <0x100>;
|
||||
interrupt-parent = <&plic>
|
||||
riscv,event-to-mhpmevent = <0x0000B 0x0000 0x0001>,
|
||||
riscv,event-to-mhpmcounters = <0x00001 0x00001 0x00000001>,
|
||||
<0x00002 0x00002 0x00000004>,
|
||||
<0x00003 0x0000A 0x00000ff8>,
|
||||
<0x10000 0x10033 0x000ff000>,
|
||||
riscv,raw-event-to-mhpmcounters = <0x0000 0x0002 0xffffffff 0xffffffff 0x00000f8>,
|
||||
<0xffffffff 0xfffffff0 0xffffffff 0xfffffff0 0x00000ff0>,
|
||||
/* For event ID 0x0002 */
|
||||
riscv,raw-event-to-mhpmcounters = <0x0000 0x0002 0xffffffff 0xffffffff 0x00000f8>,
|
||||
/* For event ID 0-4 */
|
||||
<0x0 0x0 0xffffffff 0xfffffff0 0x00000ff0>,
|
||||
/* For event ID 0xffffffff0000000f - 0xffffffff000000ff */
|
||||
<0xffffffff 0x0 0xffffffff 0xffffff0f 0x00000ff0>,
|
||||
};
|
||||
```
|
||||
|
||||
### Example 2
|
||||
|
||||
```
|
||||
/*
|
||||
* For HiFive Unmatched board. The encodings can be found here
|
||||
* https://sifive.cdn.prismic.io/sifive/1a82e600-1f93-4f41-b2d8-86ed8b16acba_fu740-c000-manual-v1p6.pdf
|
||||
*/
|
||||
pmu {
|
||||
compatible = "riscv,pmu";
|
||||
riscv,raw-event-to-mhpmcounters = <0x0 0x0 0xffffffff 0xfc0000ff 0xc>,
|
||||
<0x0 0x1 0xffffffff 0xfff800ff 0xc>,
|
||||
<0x0 0x2 0xffffffff 0xffffe0ff 0xc>;
|
||||
};
|
||||
```
|
||||
|
@@ -259,7 +259,7 @@ _bss_zero:
|
||||
* s8 -> HART Stack Size
|
||||
*/
|
||||
lla a4, platform
|
||||
#if __riscv_xlen == 64
|
||||
#if __riscv_xlen > 32
|
||||
lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4)
|
||||
lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4)
|
||||
#else
|
||||
|
@@ -75,6 +75,41 @@ struct fw_dynamic_info {
|
||||
unsigned long boot_hart;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* Prevent modification of struct fw_dynamic_info from affecting
|
||||
* FW_DYNAMIC_INFO_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, magic)
|
||||
== FW_DYNAMIC_INFO_MAGIC_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_MAGIC_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, version)
|
||||
== FW_DYNAMIC_INFO_VERSION_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, next_addr)
|
||||
== FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_NEXT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, next_mode)
|
||||
== FW_DYNAMIC_INFO_NEXT_MODE_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_NEXT_MODE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, options)
|
||||
== FW_DYNAMIC_INFO_OPTIONS_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_OPTIONS_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct fw_dynamic_info, boot_hart)
|
||||
== FW_DYNAMIC_INFO_BOOT_HART_OFFSET,
|
||||
"struct fw_dynamic_info definition has changed, please redefine "
|
||||
"FW_DYNAMIC_INFO_BOOT_HART_OFFSET");
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -25,7 +25,7 @@
|
||||
#define MSTATUS_MPP (_UL(3) << MSTATUS_MPP_SHIFT)
|
||||
#define MSTATUS_FS _UL(0x00006000)
|
||||
#define MSTATUS_XS _UL(0x00018000)
|
||||
#define MSTATUS_VS _UL(0x01800000)
|
||||
#define MSTATUS_VS _UL(0x00000600)
|
||||
#define MSTATUS_MPRV _UL(0x00020000)
|
||||
#define MSTATUS_SUM _UL(0x00040000)
|
||||
#define MSTATUS_MXR _UL(0x00080000)
|
||||
@@ -173,6 +173,10 @@
|
||||
#define HGATP_MODE_SHIFT HGATP32_MODE_SHIFT
|
||||
#endif
|
||||
|
||||
#define TOPI_IID_SHIFT 16
|
||||
#define TOPI_IID_MASK 0xfff
|
||||
#define TOPI_IPRIO_MASK 0xff
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
#define MHPMEVENT_OF (_UL(1) << 63)
|
||||
#define MHPMEVENT_MINH (_UL(1) << 62)
|
||||
@@ -181,13 +185,14 @@
|
||||
#define MHPMEVENT_VSINH (_UL(1) << 59)
|
||||
#define MHPMEVENT_VUINH (_UL(1) << 58)
|
||||
#else
|
||||
#define MHPMEVENTH_OF (_UL(1) << 31)
|
||||
#define MHPMEVENTH_OF (_ULL(1) << 31)
|
||||
#define MHPMEVENTH_MINH (_ULL(1) << 30)
|
||||
#define MHPMEVENTH_SINH (_ULL(1) << 29)
|
||||
#define MHPMEVENTH_UINH (_ULL(1) << 28)
|
||||
#define MHPMEVENTH_VSINH (_ULL(1) << 27)
|
||||
#define MHPMEVENTH_VUINH (_ULL(1) << 26)
|
||||
|
||||
#define MHPMEVENT_OF (MHPMEVENTH_OF << 32)
|
||||
#define MHPMEVENT_MINH (MHPMEVENTH_MINH << 32)
|
||||
#define MHPMEVENT_SINH (MHPMEVENTH_SINH << 32)
|
||||
#define MHPMEVENT_UINH (MHPMEVENTH_UINH << 32)
|
||||
@@ -198,6 +203,22 @@
|
||||
|
||||
#define MHPMEVENT_SSCOF_MASK _ULL(0xFFFF000000000000)
|
||||
|
||||
#if __riscv_xlen > 32
|
||||
#define ENVCFG_STCE (_ULL(1) << 63)
|
||||
#define ENVCFG_PBMTE (_ULL(1) << 62)
|
||||
#else
|
||||
#define ENVCFGH_STCE (_UL(1) << 31)
|
||||
#define ENVCFGH_PBMTE (_UL(1) << 30)
|
||||
#endif
|
||||
#define ENVCFG_CBZE (_UL(1) << 7)
|
||||
#define ENVCFG_CBCFE (_UL(1) << 6)
|
||||
#define ENVCFG_CBIE_SHIFT 4
|
||||
#define ENVCFG_CBIE (_UL(0x3) << ENVCFG_CBIE_SHIFT)
|
||||
#define ENVCFG_CBIE_ILL _UL(0x0)
|
||||
#define ENVCFG_CBIE_FLUSH _UL(0x1)
|
||||
#define ENVCFG_CBIE_INV _UL(0x3)
|
||||
#define ENVCFG_FIOM _UL(0x1)
|
||||
|
||||
/* ===== User-level CSRs ===== */
|
||||
|
||||
/* User Trap Setup (N-extension) */
|
||||
@@ -293,6 +314,9 @@
|
||||
#define CSR_STVEC 0x105
|
||||
#define CSR_SCOUNTEREN 0x106
|
||||
|
||||
/* Supervisor Configuration */
|
||||
#define CSR_SENVCFG 0x10a
|
||||
|
||||
/* Supervisor Trap Handling */
|
||||
#define CSR_SSCRATCH 0x140
|
||||
#define CSR_SEPC 0x141
|
||||
@@ -300,9 +324,31 @@
|
||||
#define CSR_STVAL 0x143
|
||||
#define CSR_SIP 0x144
|
||||
|
||||
/* Sstc extension */
|
||||
#define CSR_STIMECMP 0x14D
|
||||
#define CSR_STIMECMPH 0x15D
|
||||
|
||||
/* Supervisor Protection and Translation */
|
||||
#define CSR_SATP 0x180
|
||||
|
||||
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
#define CSR_SISELECT 0x150
|
||||
#define CSR_SIREG 0x151
|
||||
|
||||
/* Supervisor-Level Interrupts (AIA) */
|
||||
#define CSR_STOPEI 0x15c
|
||||
#define CSR_STOPI 0xdb0
|
||||
|
||||
/* Supervisor-Level High-Half CSRs (AIA) */
|
||||
#define CSR_SIEH 0x114
|
||||
#define CSR_SIPH 0x154
|
||||
|
||||
/* Supervisor stateen CSRs */
|
||||
#define CSR_SSTATEEN0 0x10C
|
||||
#define CSR_SSTATEEN1 0x10D
|
||||
#define CSR_SSTATEEN2 0x10E
|
||||
#define CSR_SSTATEEN3 0x10F
|
||||
|
||||
/* ===== Hypervisor-level CSRs ===== */
|
||||
|
||||
/* Hypervisor Trap Setup (H-extension) */
|
||||
@@ -313,6 +359,10 @@
|
||||
#define CSR_HCOUNTEREN 0x606
|
||||
#define CSR_HGEIE 0x607
|
||||
|
||||
/* Hypervisor Configuration */
|
||||
#define CSR_HENVCFG 0x60a
|
||||
#define CSR_HENVCFGH 0x61a
|
||||
|
||||
/* Hypervisor Trap Handling (H-extension) */
|
||||
#define CSR_HTVAL 0x643
|
||||
#define CSR_HIP 0x644
|
||||
@@ -338,6 +388,39 @@
|
||||
#define CSR_VSIP 0x244
|
||||
#define CSR_VSATP 0x280
|
||||
|
||||
/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
|
||||
#define CSR_HVIEN 0x608
|
||||
#define CSR_HVICTL 0x609
|
||||
#define CSR_HVIPRIO1 0x646
|
||||
#define CSR_HVIPRIO2 0x647
|
||||
|
||||
/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
|
||||
#define CSR_VSISELECT 0x250
|
||||
#define CSR_VSIREG 0x251
|
||||
|
||||
/* VS-Level Interrupts (H-extension with AIA) */
|
||||
#define CSR_VSTOPEI 0x25c
|
||||
#define CSR_VSTOPI 0xeb0
|
||||
|
||||
/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */
|
||||
#define CSR_HIDELEGH 0x613
|
||||
#define CSR_HVIENH 0x618
|
||||
#define CSR_HVIPH 0x655
|
||||
#define CSR_HVIPRIO1H 0x656
|
||||
#define CSR_HVIPRIO2H 0x657
|
||||
#define CSR_VSIEH 0x214
|
||||
#define CSR_VSIPH 0x254
|
||||
|
||||
/* Hypervisor stateen CSRs */
|
||||
#define CSR_HSTATEEN0 0x60C
|
||||
#define CSR_HSTATEEN0H 0x61C
|
||||
#define CSR_HSTATEEN1 0x60D
|
||||
#define CSR_HSTATEEN1H 0x61D
|
||||
#define CSR_HSTATEEN2 0x60E
|
||||
#define CSR_HSTATEEN2H 0x61E
|
||||
#define CSR_HSTATEEN3 0x60F
|
||||
#define CSR_HSTATEEN3H 0x61F
|
||||
|
||||
/* ===== Machine-level CSRs ===== */
|
||||
|
||||
/* Machine Information Registers */
|
||||
@@ -356,6 +439,10 @@
|
||||
#define CSR_MCOUNTEREN 0x306
|
||||
#define CSR_MSTATUSH 0x310
|
||||
|
||||
/* Machine Configuration */
|
||||
#define CSR_MENVCFG 0x30a
|
||||
#define CSR_MENVCFGH 0x31a
|
||||
|
||||
/* Machine Trap Handling */
|
||||
#define CSR_MSCRATCH 0x340
|
||||
#define CSR_MEPC 0x341
|
||||
@@ -589,6 +676,36 @@
|
||||
#define CSR_DSCRATCH0 0x7b2
|
||||
#define CSR_DSCRATCH1 0x7b3
|
||||
|
||||
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
#define CSR_MISELECT 0x350
|
||||
#define CSR_MIREG 0x351
|
||||
|
||||
/* Machine-Level Interrupts (AIA) */
|
||||
#define CSR_MTOPEI 0x35c
|
||||
#define CSR_MTOPI 0xfb0
|
||||
|
||||
/* Virtual Interrupts for Supervisor Level (AIA) */
|
||||
#define CSR_MVIEN 0x308
|
||||
#define CSR_MVIP 0x309
|
||||
|
||||
/* Smstateen extension registers */
|
||||
/* Machine stateen CSRs */
|
||||
#define CSR_MSTATEEN0 0x30C
|
||||
#define CSR_MSTATEEN0H 0x31C
|
||||
#define CSR_MSTATEEN1 0x30D
|
||||
#define CSR_MSTATEEN1H 0x31D
|
||||
#define CSR_MSTATEEN2 0x30E
|
||||
#define CSR_MSTATEEN2H 0x31E
|
||||
#define CSR_MSTATEEN3 0x30F
|
||||
#define CSR_MSTATEEN3H 0x31F
|
||||
|
||||
/* Machine-Level High-Half CSRs (AIA) */
|
||||
#define CSR_MIDELEGH 0x313
|
||||
#define CSR_MIEH 0x314
|
||||
#define CSR_MVIENH 0x318
|
||||
#define CSR_MVIPH 0x319
|
||||
#define CSR_MIPH 0x354
|
||||
|
||||
/* ===== Trap/Exception Causes ===== */
|
||||
|
||||
#define CAUSE_MISALIGNED_FETCH 0x0
|
||||
@@ -611,6 +728,23 @@
|
||||
#define CAUSE_VIRTUAL_INST_FAULT 0x16
|
||||
#define CAUSE_STORE_GUEST_PAGE_FAULT 0x17
|
||||
|
||||
/* Common defines for all smstateen */
|
||||
#define SMSTATEEN_MAX_COUNT 4
|
||||
#define SMSTATEEN0_CS_SHIFT 0
|
||||
#define SMSTATEEN0_CS (_ULL(1) << SMSTATEEN0_CS_SHIFT)
|
||||
#define SMSTATEEN0_FCSR_SHIFT 1
|
||||
#define SMSTATEEN0_FCSR (_ULL(1) << SMSTATEEN0_FCSR_SHIFT)
|
||||
#define SMSTATEEN0_IMSIC_SHIFT 58
|
||||
#define SMSTATEEN0_IMSIC (_ULL(1) << SMSTATEEN0_IMSIC_SHIFT)
|
||||
#define SMSTATEEN0_AIA_SHIFT 59
|
||||
#define SMSTATEEN0_AIA (_ULL(1) << SMSTATEEN0_AIA_SHIFT)
|
||||
#define SMSTATEEN0_SVSLCT_SHIFT 60
|
||||
#define SMSTATEEN0_SVSLCT (_ULL(1) << SMSTATEEN0_SVSLCT_SHIFT)
|
||||
#define SMSTATEEN0_HSENVCFG_SHIFT 62
|
||||
#define SMSTATEEN0_HSENVCFG (_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT)
|
||||
#define SMSTATEEN_STATEN_SHIFT 63
|
||||
#define SMSTATEEN_STATEN (_ULL(1) << SMSTATEEN_STATEN_SHIFT)
|
||||
|
||||
/* ===== Instruction Encodings ===== */
|
||||
|
||||
#define INSN_MATCH_LB 0x3
|
||||
@@ -686,6 +820,29 @@
|
||||
#define INSN_MASK_WFI 0xffffff00
|
||||
#define INSN_MATCH_WFI 0x10500000
|
||||
|
||||
#define INSN_MASK_FENCE_TSO 0xffffffff
|
||||
#define INSN_MATCH_FENCE_TSO 0x8330000f
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
|
||||
/* 64-bit read for VS-stage address translation (RV64) */
|
||||
#define INSN_PSEUDO_VS_LOAD 0x00003000
|
||||
|
||||
/* 64-bit write for VS-stage address translation (RV64) */
|
||||
#define INSN_PSEUDO_VS_STORE 0x00003020
|
||||
|
||||
#elif __riscv_xlen == 32
|
||||
|
||||
/* 32-bit read for VS-stage address translation (RV32) */
|
||||
#define INSN_PSEUDO_VS_LOAD 0x00002000
|
||||
|
||||
/* 32-bit write for VS-stage address translation (RV32) */
|
||||
#define INSN_PSEUDO_VS_STORE 0x00002020
|
||||
|
||||
#else
|
||||
#error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
|
||||
#define INSN_16BIT_MASK 0x3
|
||||
#define INSN_32BIT_MASK 0x1c
|
||||
|
||||
|
@@ -37,47 +37,12 @@
|
||||
(((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
|
||||
|
||||
/**
|
||||
* ffs - Find first bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This is defined the same way as
|
||||
* the libc and compiler builtin ffs routines, therefore
|
||||
* differs in spirit from the above ffz (man ffs).
|
||||
*/
|
||||
static inline int ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff)) {
|
||||
x >>= 16;
|
||||
r += 16;
|
||||
}
|
||||
if (!(x & 0xff)) {
|
||||
x >>= 8;
|
||||
r += 8;
|
||||
}
|
||||
if (!(x & 0xf)) {
|
||||
x >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (!(x & 3)) {
|
||||
x >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (!(x & 1))
|
||||
r += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* __ffs - find first bit in word.
|
||||
* sbi_ffs - find first (less-significant) set bit in a long word.
|
||||
* @word: The word to search
|
||||
*
|
||||
* Undefined if no bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline int __ffs(unsigned long word)
|
||||
static inline int sbi_ffs(unsigned long word)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
@@ -109,55 +74,20 @@ static inline int __ffs(unsigned long word)
|
||||
}
|
||||
|
||||
/*
|
||||
* ffz - find first zero in word.
|
||||
* sbi_ffz - find first zero in word.
|
||||
* @word: The word to search
|
||||
*
|
||||
* Undefined if no zero exists, so code should check against ~0UL first.
|
||||
*/
|
||||
#define ffz(x) __ffs(~(x))
|
||||
#define sbi_ffz(x) sbi_ffs(~(x))
|
||||
|
||||
/**
|
||||
* fls - find last (most-significant) bit set
|
||||
* @x: the word to search
|
||||
*
|
||||
* This is defined the same way as ffs.
|
||||
* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
|
||||
*/
|
||||
|
||||
static inline int fls(int x)
|
||||
{
|
||||
int r = 32;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff0000u)) {
|
||||
x <<= 16;
|
||||
r -= 16;
|
||||
}
|
||||
if (!(x & 0xff000000u)) {
|
||||
x <<= 8;
|
||||
r -= 8;
|
||||
}
|
||||
if (!(x & 0xf0000000u)) {
|
||||
x <<= 4;
|
||||
r -= 4;
|
||||
}
|
||||
if (!(x & 0xc0000000u)) {
|
||||
x <<= 2;
|
||||
r -= 2;
|
||||
}
|
||||
if (!(x & 0x80000000u))
|
||||
r -= 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* __fls - find last (most-significant) set bit in a long word
|
||||
* sbi_fls - find last (most-significant) set bit in a long word
|
||||
* @word: the word to search
|
||||
*
|
||||
* Undefined if no set bit exists, so code should check against 0 first.
|
||||
*/
|
||||
static inline unsigned long __fls(unsigned long word)
|
||||
static inline unsigned long sbi_fls(unsigned long word)
|
||||
{
|
||||
int num = BITS_PER_LONG - 1;
|
||||
|
||||
|
@@ -174,7 +174,9 @@ int sbi_domain_register(struct sbi_domain *dom,
|
||||
* Add a memory region to the root domain
|
||||
* @param reg pointer to the memory region to be added
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
* @return 0 on success
|
||||
* @return SBI_EALREADY if memory region conflicts with existing
|
||||
* @return SBI_EINVAL otherwise
|
||||
*/
|
||||
int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg);
|
||||
|
||||
|
@@ -13,8 +13,8 @@
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_list.h>
|
||||
|
||||
#define SBI_ECALL_VERSION_MAJOR 0
|
||||
#define SBI_ECALL_VERSION_MINOR 3
|
||||
#define SBI_ECALL_VERSION_MAJOR 1
|
||||
#define SBI_ECALL_VERSION_MINOR 0
|
||||
#define SBI_OPENSBI_IMPID 1
|
||||
|
||||
struct sbi_trap_regs;
|
||||
|
@@ -12,21 +12,33 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
/** Possible feature flags of a hart */
|
||||
enum sbi_hart_features {
|
||||
/** Hart has S-mode counter enable */
|
||||
SBI_HART_HAS_SCOUNTEREN = (1 << 0),
|
||||
/** Hart has M-mode counter enable */
|
||||
SBI_HART_HAS_MCOUNTEREN = (1 << 1),
|
||||
/** Hart has counter inhibit CSR */
|
||||
SBI_HART_HAS_MCOUNTINHIBIT = (1 << 2),
|
||||
/** Hart has sscofpmf extension */
|
||||
SBI_HART_HAS_SSCOFPMF = (1 << 3),
|
||||
/** HART has timer csr implementation in hardware */
|
||||
SBI_HART_HAS_TIME = (1 << 4),
|
||||
/** Possible privileged specification versions of a hart */
|
||||
enum sbi_hart_priv_versions {
|
||||
/** Unknown privileged specification */
|
||||
SBI_HART_PRIV_VER_UNKNOWN = 0,
|
||||
/** Privileged specification v1.10 */
|
||||
SBI_HART_PRIV_VER_1_10 = 1,
|
||||
/** Privileged specification v1.11 */
|
||||
SBI_HART_PRIV_VER_1_11 = 2,
|
||||
/** Privileged specification v1.12 */
|
||||
SBI_HART_PRIV_VER_1_12 = 3,
|
||||
};
|
||||
|
||||
/** Last index of Hart features*/
|
||||
SBI_HART_HAS_LAST_FEATURE = SBI_HART_HAS_TIME,
|
||||
/** Possible ISA extensions of a hart */
|
||||
enum sbi_hart_extensions {
|
||||
/** Hart has Sscofpmt extension */
|
||||
SBI_HART_EXT_SSCOFPMF = 0,
|
||||
/** HART has HW time CSR (extension name not available) */
|
||||
SBI_HART_EXT_TIME,
|
||||
/** HART has AIA CSRs (extension name not available) */
|
||||
SBI_HART_EXT_AIA,
|
||||
/** HART has Smstateen CSR **/
|
||||
SBI_HART_EXT_SMSTATEEN,
|
||||
/** HART has Sstc extension */
|
||||
SBI_HART_EXT_SSTC,
|
||||
|
||||
/** Maximum index of Hart extension */
|
||||
SBI_HART_EXT_MAX,
|
||||
};
|
||||
|
||||
struct sbi_scratch;
|
||||
@@ -48,9 +60,16 @@ unsigned long sbi_hart_pmp_granularity(struct sbi_scratch *scratch);
|
||||
unsigned int sbi_hart_pmp_addrbits(struct sbi_scratch *scratch);
|
||||
unsigned int sbi_hart_mhpm_bits(struct sbi_scratch *scratch);
|
||||
int sbi_hart_pmp_configure(struct sbi_scratch *scratch);
|
||||
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature);
|
||||
void sbi_hart_get_features_str(struct sbi_scratch *scratch,
|
||||
char *features_str, int nfstr);
|
||||
int sbi_hart_priv_version(struct sbi_scratch *scratch);
|
||||
void sbi_hart_get_priv_version_str(struct sbi_scratch *scratch,
|
||||
char *version_str, int nvstr);
|
||||
void sbi_hart_update_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable);
|
||||
bool sbi_hart_has_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext);
|
||||
void sbi_hart_get_extensions_str(struct sbi_scratch *scratch,
|
||||
char *extension_str, int nestr);
|
||||
|
||||
void __attribute__((noreturn)) sbi_hart_hang(void);
|
||||
|
||||
|
@@ -34,9 +34,17 @@ struct sbi_hsm_device {
|
||||
* the hart resumes normal execution.
|
||||
*
|
||||
* For successful non-retentive suspend, the hart will resume from
|
||||
* specified resume address
|
||||
* the warm boot entry point.
|
||||
*/
|
||||
int (*hart_suspend)(u32 suspend_type, ulong raddr);
|
||||
int (*hart_suspend)(u32 suspend_type);
|
||||
|
||||
/**
|
||||
* Perform platform-specific actions to resume from a suspended state.
|
||||
*
|
||||
* This includes restoring any platform state that was lost during
|
||||
* non-retentive suspend.
|
||||
*/
|
||||
void (*hart_resume)(void);
|
||||
};
|
||||
|
||||
struct sbi_domain;
|
||||
|
44
include/sbi/sbi_irqchip.h
Normal file
44
include/sbi/sbi_irqchip.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <apatel@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#ifndef __SBI_IRQCHIP_H__
|
||||
#define __SBI_IRQCHIP_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
struct sbi_scratch;
|
||||
struct sbi_trap_regs;
|
||||
|
||||
/**
|
||||
* Set external interrupt handling function
|
||||
*
|
||||
* This function is called by OpenSBI platform code to set a handler for
|
||||
* external interrupts
|
||||
*
|
||||
* @param fn function pointer for handling external irqs
|
||||
*/
|
||||
void sbi_irqchip_set_irqfn(int (*fn)(struct sbi_trap_regs *regs));
|
||||
|
||||
/**
|
||||
* Process external interrupts
|
||||
*
|
||||
* This function is called by sbi_trap_handler() to handle external
|
||||
* interrupts.
|
||||
*
|
||||
* @param regs pointer for trap registers
|
||||
*/
|
||||
int sbi_irqchip_process(struct sbi_trap_regs *regs);
|
||||
|
||||
/** Initialize interrupt controllers */
|
||||
int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot);
|
||||
|
||||
/** Exit interrupt controllers */
|
||||
void sbi_irqchip_exit(struct sbi_scratch *scratch);
|
||||
|
||||
#endif
|
@@ -64,6 +64,9 @@ enum sbi_platform_features {
|
||||
|
||||
/** Platform functions */
|
||||
struct sbi_platform_operations {
|
||||
/* Platform nascent initialization */
|
||||
int (*nascent_init)(void);
|
||||
|
||||
/** Platform early initialization */
|
||||
int (*early_init)(bool cold_boot);
|
||||
/** Platform final initialization */
|
||||
@@ -86,6 +89,9 @@ struct sbi_platform_operations {
|
||||
*/
|
||||
int (*misa_get_xlen)(void);
|
||||
|
||||
/** Initialize (or populate) HART extensions for the platform */
|
||||
int (*extensions_init)(void);
|
||||
|
||||
/** Initialize (or populate) domains for the platform */
|
||||
int (*domains_init)(void);
|
||||
|
||||
@@ -172,6 +178,56 @@ struct sbi_platform {
|
||||
const u32 *hart_index2id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent modification of struct sbi_platform from affecting
|
||||
* SBI_PLATFORM_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, opensbi_version)
|
||||
== SBI_PLATFORM_OPENSBI_VERSION_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_OPENSBI_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, platform_version)
|
||||
== SBI_PLATFORM_VERSION_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_VERSION_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, name)
|
||||
== SBI_PLATFORM_NAME_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_NAME_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, features)
|
||||
== SBI_PLATFORM_FEATURES_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_FEATURES_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_count)
|
||||
== SBI_PLATFORM_HART_COUNT_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_COUNT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_stack_size)
|
||||
== SBI_PLATFORM_HART_STACK_SIZE_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_STACK_SIZE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, platform_ops_addr)
|
||||
== SBI_PLATFORM_OPS_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_OPS_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, firmware_context)
|
||||
== SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_FIRMWARE_CONTEXT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_platform, hart_index2id)
|
||||
== SBI_PLATFORM_HART_INDEX2ID_OFFSET,
|
||||
"struct sbi_platform definition has changed, please redefine "
|
||||
"SBI_PLATFORM_HART_INDEX2ID_OFFSET");
|
||||
|
||||
/** Get pointer to sbi_platform for sbi_scratch pointer */
|
||||
#define sbi_platform_ptr(__s) \
|
||||
((const struct sbi_platform *)((__s)->platform_addr))
|
||||
@@ -299,6 +355,23 @@ static inline bool sbi_platform_hart_invalid(const struct sbi_platform *plat,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nascent (very early) initialization for current HART
|
||||
*
|
||||
* NOTE: This function can be used to do very early initialization of
|
||||
* platform specific per-HART CSRs and devices.
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_nascent_init(const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->nascent_init)
|
||||
return sbi_platform_ops(plat)->nascent_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initialization for current HART
|
||||
*
|
||||
@@ -383,6 +456,21 @@ static inline int sbi_platform_misa_xlen(const struct sbi_platform *plat)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (or populate) HART extensions for the platform
|
||||
*
|
||||
* @param plat pointer to struct sbi_platform
|
||||
*
|
||||
* @return 0 on success and negative error code on failure
|
||||
*/
|
||||
static inline int sbi_platform_extensions_init(
|
||||
const struct sbi_platform *plat)
|
||||
{
|
||||
if (plat && sbi_platform_ops(plat)->extensions_init)
|
||||
return sbi_platform_ops(plat)->extensions_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (or populate) domains for the platform
|
||||
*
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
/* Event related macros */
|
||||
/* Maximum number of hardware events that can mapped by OpenSBI */
|
||||
#define SBI_PMU_HW_EVENT_MAX 64
|
||||
#define SBI_PMU_HW_EVENT_MAX 256
|
||||
|
||||
/* Maximum number of firmware events that can mapped by OpenSBI */
|
||||
#define SBI_PMU_FW_EVENT_MAX 32
|
||||
|
@@ -73,6 +73,66 @@ struct sbi_scratch {
|
||||
unsigned long options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevent modification of struct sbi_scratch from affecting
|
||||
* SBI_SCRATCH_xxx_OFFSET
|
||||
*/
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, fw_start)
|
||||
== SBI_SCRATCH_FW_START_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_FW_START_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, fw_size)
|
||||
== SBI_SCRATCH_FW_SIZE_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_FW_SIZE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_arg1)
|
||||
== SBI_SCRATCH_NEXT_ARG1_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_ARG1_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_addr)
|
||||
== SBI_SCRATCH_NEXT_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, next_mode)
|
||||
== SBI_SCRATCH_NEXT_MODE_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_NEXT_MODE_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, warmboot_addr)
|
||||
== SBI_SCRATCH_WARMBOOT_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_WARMBOOT_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, platform_addr)
|
||||
== SBI_SCRATCH_PLATFORM_ADDR_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_PLATFORM_ADDR_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, hartid_to_scratch)
|
||||
== SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, trap_exit)
|
||||
== SBI_SCRATCH_TRAP_EXIT_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_TRAP_EXIT_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, tmp0)
|
||||
== SBI_SCRATCH_TMP0_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_TMP0_OFFSET");
|
||||
_Static_assert(
|
||||
offsetof(struct sbi_scratch, options)
|
||||
== SBI_SCRATCH_OPTIONS_OFFSET,
|
||||
"struct sbi_scratch definition has changed, please redefine "
|
||||
"SBI_SCRATCH_OPTIONS_OFFSET");
|
||||
|
||||
/** Possible options for OpenSBI library */
|
||||
enum sbi_scratch_options {
|
||||
/** Disable prints during boot */
|
||||
@@ -104,11 +164,11 @@ unsigned long sbi_scratch_alloc_offset(unsigned long size);
|
||||
void sbi_scratch_free_offset(unsigned long offset);
|
||||
|
||||
/** Get pointer from offset in sbi_scratch */
|
||||
#define sbi_scratch_offset_ptr(scratch, offset) ((void *)scratch + (offset))
|
||||
#define sbi_scratch_offset_ptr(scratch, offset) (void *)((char *)(scratch) + (offset))
|
||||
|
||||
/** Get pointer from offset in sbi_scratch for current HART */
|
||||
#define sbi_scratch_thishart_offset_ptr(offset) \
|
||||
((void *)sbi_scratch_thishart_ptr() + (offset))
|
||||
(void *)((char *)sbi_scratch_thishart_ptr() + (offset))
|
||||
|
||||
/** HART id to scratch table */
|
||||
extern struct sbi_scratch *hartid_to_scratch_table[];
|
||||
|
@@ -11,7 +11,7 @@
|
||||
#define __SBI_VERSION_H__
|
||||
|
||||
#define OPENSBI_VERSION_MAJOR 1
|
||||
#define OPENSBI_VERSION_MINOR 0
|
||||
#define OPENSBI_VERSION_MINOR 1
|
||||
|
||||
/**
|
||||
* OpenSBI 32-bit version with:
|
||||
|
@@ -21,6 +21,30 @@
|
||||
*/
|
||||
void fdt_cpu_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the APLIC nodes in the device tree
|
||||
*
|
||||
* This routine disables APLIC nodes which are not accessible to the next
|
||||
* booting stage based on currently assigned domain.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
* @param fdt: device tree blob
|
||||
*/
|
||||
void fdt_aplic_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the IMSIC nodes in the device tree
|
||||
*
|
||||
* This routine disables IMSIC nodes which are not accessible to the next
|
||||
* booting stage based on currently assigned domain.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
* @param fdt: device tree blob
|
||||
*/
|
||||
void fdt_imsic_fixup(void *fdt);
|
||||
|
||||
/**
|
||||
* Fix up the PLIC node in the device tree
|
||||
*
|
||||
@@ -64,8 +88,9 @@ int fdt_reserved_memory_nomap_fixup(void *fdt);
|
||||
* General device tree fix-up
|
||||
*
|
||||
* This routine do all required device tree fix-ups for a typical platform.
|
||||
* It fixes up the PLIC node and the reserved memory node in the device tree
|
||||
* by calling the corresponding helper routines to accomplish the task.
|
||||
* It fixes up the PLIC node, IMSIC nodes, APLIC nodes, and the reserved
|
||||
* memory node in the device tree by calling the corresponding helper
|
||||
* routines to accomplish the task.
|
||||
*
|
||||
* It is recommended that platform codes call this helper in their final_init()
|
||||
*
|
||||
|
@@ -15,7 +15,7 @@
|
||||
|
||||
struct fdt_match {
|
||||
const char *compatible;
|
||||
void *data;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
#define FDT_MAX_PHANDLE_ARGS 16
|
||||
@@ -31,6 +31,7 @@ struct platform_uart_data {
|
||||
unsigned long baud;
|
||||
unsigned long reg_shift;
|
||||
unsigned long reg_io_width;
|
||||
unsigned long reg_offset;
|
||||
};
|
||||
|
||||
const struct fdt_match *fdt_match_node(void *fdt, int nodeoff,
|
||||
@@ -47,9 +48,11 @@ int fdt_parse_phandle_with_args(void *fdt, int nodeoff,
|
||||
int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
uint64_t *addr, uint64_t *size);
|
||||
|
||||
bool fdt_node_is_enabled(void *fdt, int nodeoff);
|
||||
|
||||
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid);
|
||||
|
||||
int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
|
||||
int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid);
|
||||
|
||||
int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
|
||||
|
||||
@@ -68,6 +71,19 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
|
||||
const char *compatible);
|
||||
|
||||
int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart);
|
||||
|
||||
struct aplic_data;
|
||||
|
||||
int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic);
|
||||
|
||||
struct imsic_data;
|
||||
|
||||
bool fdt_check_imsic_mlevel(void *fdt);
|
||||
|
||||
int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic);
|
||||
|
||||
struct plic_data;
|
||||
|
||||
int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic);
|
||||
|
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <sbi_utils/gpio/gpio.h>
|
||||
|
||||
struct fdt_phandle_args;
|
||||
|
||||
/** FDT based GPIO driver */
|
||||
struct fdt_gpio {
|
||||
const struct fdt_match *match_table;
|
||||
|
47
include/sbi_utils/irqchip/aplic.h
Normal file
47
include/sbi_utils/irqchip/aplic.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_APLIC_H__
|
||||
#define __IRQCHIP_APLIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define APLIC_MAX_DELEGATE 16
|
||||
|
||||
struct aplic_msicfg_data {
|
||||
unsigned long lhxs;
|
||||
unsigned long lhxw;
|
||||
unsigned long hhxs;
|
||||
unsigned long hhxw;
|
||||
unsigned long base_addr;
|
||||
};
|
||||
|
||||
struct aplic_delegate_data {
|
||||
u32 first_irq;
|
||||
u32 last_irq;
|
||||
u32 child_index;
|
||||
};
|
||||
|
||||
struct aplic_data {
|
||||
unsigned long addr;
|
||||
unsigned long size;
|
||||
unsigned long num_idc;
|
||||
unsigned long num_source;
|
||||
bool targets_mmode;
|
||||
bool has_msicfg_mmode;
|
||||
struct aplic_msicfg_data msicfg_mmode;
|
||||
bool has_msicfg_smode;
|
||||
struct aplic_msicfg_data msicfg_smode;
|
||||
struct aplic_delegate_data delegate[APLIC_MAX_DELEGATE];
|
||||
};
|
||||
|
||||
int aplic_cold_irqchip_init(struct aplic_data *aplic);
|
||||
|
||||
#endif
|
22
include/sbi_utils/irqchip/fdt_irqchip_plic.h
Normal file
22
include/sbi_utils/irqchip/fdt_irqchip_plic.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Samuel Holland <samuel@sholland.org>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_FDT_IRQCHIP_PLIC_H__
|
||||
#define __IRQCHIP_FDT_IRQCHIP_PLIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
void fdt_plic_priority_save(u8 *priority);
|
||||
|
||||
void fdt_plic_priority_restore(const u8 *priority);
|
||||
|
||||
void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold);
|
||||
|
||||
void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold);
|
||||
|
||||
void thead_plic_restore(void);
|
||||
|
||||
#endif
|
50
include/sbi_utils/irqchip/imsic.h
Normal file
50
include/sbi_utils/irqchip/imsic.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#ifndef __IRQCHIP_IMSIC_H__
|
||||
#define __IRQCHIP_IMSIC_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
#define IMSIC_MMIO_PAGE_SHIFT 12
|
||||
#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT)
|
||||
|
||||
#define IMSIC_MAX_REGS 16
|
||||
|
||||
struct imsic_regs {
|
||||
unsigned long addr;
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
struct imsic_data {
|
||||
bool targets_mmode;
|
||||
u32 guest_index_bits;
|
||||
u32 hart_index_bits;
|
||||
u32 group_index_bits;
|
||||
u32 group_index_shift;
|
||||
unsigned long num_ids;
|
||||
struct imsic_regs regs[IMSIC_MAX_REGS];
|
||||
};
|
||||
|
||||
int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
|
||||
|
||||
struct imsic_data *imsic_get_data(u32 hartid);
|
||||
|
||||
int imsic_get_target_file(u32 hartid);
|
||||
|
||||
void imsic_local_irqchip_init(void);
|
||||
|
||||
int imsic_warm_irqchip_init(void);
|
||||
|
||||
int imsic_data_check(struct imsic_data *imsic);
|
||||
|
||||
int imsic_cold_irqchip_init(struct imsic_data *imsic);
|
||||
|
||||
#endif
|
@@ -17,13 +17,23 @@ struct plic_data {
|
||||
unsigned long num_src;
|
||||
};
|
||||
|
||||
int plic_warm_irqchip_init(struct plic_data *plic,
|
||||
/* So far, priorities on all consumers of these functions fit in 8 bits. */
|
||||
void plic_priority_save(const struct plic_data *plic, u8 *priority);
|
||||
|
||||
void plic_priority_restore(const struct plic_data *plic, const u8 *priority);
|
||||
|
||||
void plic_context_save(const struct plic_data *plic, int context_id,
|
||||
u32 *enable, u32 *threshold);
|
||||
|
||||
void plic_context_restore(const struct plic_data *plic, int context_id,
|
||||
const u32 *enable, u32 threshold);
|
||||
|
||||
int plic_context_init(const struct plic_data *plic, int context_id,
|
||||
bool enable, u32 threshold);
|
||||
|
||||
int plic_warm_irqchip_init(const struct plic_data *plic,
|
||||
int m_cntx_id, int s_cntx_id);
|
||||
|
||||
int plic_cold_irqchip_init(struct plic_data *plic);
|
||||
|
||||
void plic_set_thresh(struct plic_data *plic, u32 cntxid, u32 val);
|
||||
|
||||
void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val);
|
||||
int plic_cold_irqchip_init(const struct plic_data *plic);
|
||||
|
||||
#endif
|
||||
|
@@ -13,6 +13,6 @@
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
|
||||
u32 reg_width);
|
||||
u32 reg_width, u32 reg_offset);
|
||||
|
||||
#endif
|
||||
|
16
include/sbi_utils/serial/xlnx_uartlite.h
Normal file
16
include/sbi_utils/serial/xlnx_uartlite.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Alistair Francis <alistair.francis@wdc.com>
|
||||
*/
|
||||
#ifndef __SERIAL_XLNX_UARTLITE_H__
|
||||
#define __SERIAL_XLNX_UARTLITE_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int xlnx_uartlite_init(unsigned long base);
|
||||
|
||||
#endif
|
@@ -10,8 +10,12 @@
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
|
||||
int htif_serial_init(void);
|
||||
int htif_serial_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr);
|
||||
|
||||
int htif_system_reset_init(void);
|
||||
int htif_system_reset_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr);
|
||||
|
||||
#endif
|
||||
|
@@ -32,6 +32,7 @@ libsbi-objs-y += sbi_hsm.o
|
||||
libsbi-objs-y += sbi_illegal_insn.o
|
||||
libsbi-objs-y += sbi_init.o
|
||||
libsbi-objs-y += sbi_ipi.o
|
||||
libsbi-objs-y += sbi_irqchip.o
|
||||
libsbi-objs-y += sbi_misaligned_ldst.o
|
||||
libsbi-objs-y += sbi_platform.o
|
||||
libsbi-objs-y += sbi_pmu.o
|
||||
|
@@ -53,7 +53,7 @@ int misa_xlen(void)
|
||||
void misa_string(int xlen, char *out, unsigned int out_sz)
|
||||
{
|
||||
unsigned int i, pos = 0;
|
||||
const char valid_isa_order[] = "iemafdqclbjtpvnsuhkorwxyzg";
|
||||
const char valid_isa_order[] = "iemafdqclbjtpvnhkorwxyzg";
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
@@ -139,6 +139,10 @@ unsigned long csr_read_num(int csr_num)
|
||||
switchcase_csr_read_4(CSR_MHPMCOUNTER4H, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMCOUNTER8H, ret)
|
||||
switchcase_csr_read_16(CSR_MHPMCOUNTER16H, ret)
|
||||
/**
|
||||
* The CSR range MHPMEVENT[3-16]H are available only if sscofpmf
|
||||
* extension is present. The caller must ensure that.
|
||||
*/
|
||||
switchcase_csr_read(CSR_MHPMEVENT3H, ret)
|
||||
switchcase_csr_read_4(CSR_MHPMEVENT4H, ret)
|
||||
switchcase_csr_read_8(CSR_MHPMEVENT8H, ret)
|
||||
@@ -261,7 +265,7 @@ int pmp_set(unsigned int n, unsigned long prot, unsigned long addr,
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
return SBI_ENOTSUPP;
|
||||
# error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
|
||||
@@ -312,7 +316,7 @@ int pmp_get(unsigned int n, unsigned long *prot_out, unsigned long *addr_out,
|
||||
pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
|
||||
pmpcfg_shift = (n & 7) << 3;
|
||||
#else
|
||||
return SBI_ENOTSUPP;
|
||||
# error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
pmpaddr_csr = CSR_PMPADDR0 + n;
|
||||
|
||||
|
@@ -39,7 +39,7 @@ unsigned long find_first_bit(const unsigned long *addr,
|
||||
if (tmp == 0UL) /* Are any bits set? */
|
||||
return result + size; /* Nope. */
|
||||
found:
|
||||
return result + __ffs(tmp);
|
||||
return result + sbi_ffs(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ unsigned long find_first_zero_bit(const unsigned long *addr,
|
||||
if (tmp == ~0UL) /* Are any bits zero? */
|
||||
return result + size; /* Nope. */
|
||||
found:
|
||||
return result + ffz(tmp);
|
||||
return result + sbi_ffz(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,7 @@ unsigned long find_last_bit(const unsigned long *addr,
|
||||
tmp = addr[--words];
|
||||
if (tmp) {
|
||||
found:
|
||||
return words * BITS_PER_LONG + __fls(tmp);
|
||||
return words * BITS_PER_LONG + sbi_fls(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ found_first:
|
||||
if (tmp == 0UL) /* Are any bits set? */
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + __ffs(tmp);
|
||||
return result + sbi_ffs(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,5 +196,5 @@ found_first:
|
||||
if (tmp == ~0UL) /* Are any bits zero? */
|
||||
return result + size; /* Nope. */
|
||||
found_middle:
|
||||
return result + ffz(tmp);
|
||||
return result + sbi_ffz(tmp);
|
||||
}
|
||||
|
@@ -159,7 +159,7 @@ static bool is_region_subset(const struct sbi_domain_memregion *regA,
|
||||
ulong regA_start = regA->base;
|
||||
ulong regA_end = regA->base + (BIT(regA->order) - 1);
|
||||
ulong regB_start = regB->base;
|
||||
ulong regB_end = regB->base + (BIT(regA->order) - 1);
|
||||
ulong regB_end = regB->base + (BIT(regB->order) - 1);
|
||||
|
||||
if ((regB_start <= regA_start) &&
|
||||
(regA_start < regB_end) &&
|
||||
@@ -471,8 +471,12 @@ int sbi_domain_root_add_memregion(const struct sbi_domain_memregion *reg)
|
||||
|
||||
/* Check for conflicts */
|
||||
sbi_domain_for_each_memregion(&root, nreg) {
|
||||
if (is_region_conflict(reg, nreg))
|
||||
return SBI_EINVAL;
|
||||
if (is_region_conflict(reg, nreg)) {
|
||||
sbi_printf("%s: is_region_conflict check failed"
|
||||
" 0x%lx conflicts existing 0x%lx\n", __func__,
|
||||
reg->base, nreg->base);
|
||||
return SBI_EALREADY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append the memregion to root memregions */
|
||||
|
@@ -24,7 +24,7 @@ static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
if (prev_mode <= PRV_S) {
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN)) {
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) {
|
||||
cen &= csr_read(CSR_MCOUNTEREN);
|
||||
if (virt)
|
||||
cen &= csr_read(CSR_HCOUNTEREN);
|
||||
@@ -33,7 +33,7 @@ static bool hpm_allowed(int hpm_num, ulong prev_mode, bool virt)
|
||||
}
|
||||
}
|
||||
if (prev_mode == PRV_U) {
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
cen &= csr_read(CSR_SCOUNTEREN);
|
||||
else
|
||||
cen = 0;
|
||||
|
@@ -66,7 +66,7 @@ static inline void __sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data)
|
||||
if (head >= fifo->num_entries)
|
||||
head = head - fifo->num_entries;
|
||||
|
||||
sbi_memcpy(fifo->queue + head * fifo->entry_size, data, fifo->entry_size);
|
||||
sbi_memcpy((char *)fifo->queue + head * fifo->entry_size, data, fifo->entry_size);
|
||||
|
||||
fifo->avail++;
|
||||
}
|
||||
@@ -142,7 +142,7 @@ int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
|
||||
index = fifo->tail + i;
|
||||
if (index >= fifo->num_entries)
|
||||
index -= fifo->num_entries;
|
||||
entry = (void *)fifo->queue + (u32)index * fifo->entry_size;
|
||||
entry = (char *)fifo->queue + (u32)index * fifo->entry_size;
|
||||
ret = fptr(in, entry);
|
||||
|
||||
if (ret == SBI_FIFO_SKIP || ret == SBI_FIFO_UPDATED) {
|
||||
@@ -184,7 +184,7 @@ int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data)
|
||||
return SBI_ENOENT;
|
||||
}
|
||||
|
||||
sbi_memcpy(data, fifo->queue + (u32)fifo->tail * fifo->entry_size,
|
||||
sbi_memcpy(data, (char *)fifo->queue + (u32)fifo->tail * fifo->entry_size,
|
||||
fifo->entry_size);
|
||||
|
||||
fifo->avail--;
|
||||
|
@@ -28,7 +28,9 @@ extern void __sbi_expected_trap_hext(void);
|
||||
void (*sbi_hart_expected_trap)(void) = &__sbi_expected_trap;
|
||||
|
||||
struct hart_features {
|
||||
unsigned long features;
|
||||
bool detected;
|
||||
int priv_version;
|
||||
unsigned long extensions;
|
||||
unsigned int pmp_count;
|
||||
unsigned int pmp_addr_bits;
|
||||
unsigned long pmp_gran;
|
||||
@@ -39,7 +41,11 @@ static unsigned long hart_features_offset;
|
||||
|
||||
static void mstatus_init(struct sbi_scratch *scratch)
|
||||
{
|
||||
unsigned long mstatus_val = 0;
|
||||
unsigned long menvcfg_val, mstatus_val = 0;
|
||||
int cidx;
|
||||
unsigned int num_mhpm = sbi_hart_mhpm_count(scratch);
|
||||
uint64_t mhpmevent_init_val = 0;
|
||||
uint64_t mstateen_val;
|
||||
|
||||
/* Enable FPU */
|
||||
if (misa_extension('D') || misa_extension('F'))
|
||||
@@ -53,7 +59,7 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
|
||||
/* Disable user mode usage of all perf counters except default ones (CY, TM, IR) */
|
||||
if (misa_extension('S') &&
|
||||
sbi_hart_has_feature(scratch, SBI_HART_HAS_SCOUNTEREN))
|
||||
sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_SCOUNTEREN, 7);
|
||||
|
||||
/**
|
||||
@@ -61,13 +67,107 @@ static void mstatus_init(struct sbi_scratch *scratch)
|
||||
* Supervisor mode usage for all counters are enabled by default
|
||||
* But counters will not run until mcountinhibit is set.
|
||||
*/
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTEREN))
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
|
||||
/* All programmable counters will start running at runtime after S-mode request */
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
/**
|
||||
* The mhpmeventn[h] CSR should be initialized with interrupt disabled
|
||||
* and inhibited running in M-mode during init.
|
||||
* To keep it simple, only contiguous mhpmcounters are supported as a
|
||||
* platform with discontiguous mhpmcounters may not make much sense.
|
||||
*/
|
||||
mhpmevent_init_val |= (MHPMEVENT_OF | MHPMEVENT_MINH);
|
||||
for (cidx = 0; cidx < num_mhpm; cidx++) {
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + cidx, mhpmevent_init_val & 0xFFFFFFFF);
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + cidx,
|
||||
mhpmevent_init_val >> BITS_PER_LONG);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + cidx, mhpmevent_init_val);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMSTATEEN)) {
|
||||
mstateen_val = csr_read(CSR_MSTATEEN0);
|
||||
#if __riscv_xlen == 32
|
||||
mstateen_val |= ((uint64_t)csr_read(CSR_MSTATEEN0H)) << 32;
|
||||
#endif
|
||||
mstateen_val |= SMSTATEEN_STATEN;
|
||||
mstateen_val |= SMSTATEEN0_HSENVCFG;
|
||||
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_AIA))
|
||||
mstateen_val |= (SMSTATEEN0_AIA | SMSTATEEN0_SVSLCT |
|
||||
SMSTATEEN0_IMSIC);
|
||||
else
|
||||
mstateen_val &= ~(SMSTATEEN0_AIA | SMSTATEEN0_SVSLCT |
|
||||
SMSTATEEN0_IMSIC);
|
||||
csr_write(CSR_MSTATEEN0, mstateen_val);
|
||||
#if __riscv_xlen == 32
|
||||
csr_write(CSR_MSTATEEN0H, mstateen_val >> 32);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12) {
|
||||
menvcfg_val = csr_read(CSR_MENVCFG);
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBZE == 1
|
||||
*
|
||||
* If Zicboz extension is not available then writes to
|
||||
* menvcfg.CBZE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBZE;
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBCFE == 1
|
||||
*
|
||||
* If Zicbom extension is not available then writes to
|
||||
* menvcfg.CBCFE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBCFE;
|
||||
|
||||
/*
|
||||
* Set menvcfg.CBIE == 3
|
||||
*
|
||||
* If Zicbom extension is not available then writes to
|
||||
* menvcfg.CBIE will be ignored because it is a WARL field.
|
||||
*/
|
||||
menvcfg_val |= ENVCFG_CBIE_INV << ENVCFG_CBIE_SHIFT;
|
||||
|
||||
/*
|
||||
* Set menvcfg.PBMTE == 1 for RV64 or RV128
|
||||
*
|
||||
* If Svpbmt extension is not available then menvcfg.PBMTE
|
||||
* will be read-only zero.
|
||||
*/
|
||||
#if __riscv_xlen > 32
|
||||
menvcfg_val |= ENVCFG_PBMTE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The spec doesn't explicitly describe the reset value of menvcfg.
|
||||
* Enable access to stimecmp if sstc extension is present in the
|
||||
* hardware.
|
||||
*/
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSTC)) {
|
||||
#if __riscv_xlen == 32
|
||||
unsigned long menvcfgh_val;
|
||||
menvcfgh_val = csr_read(CSR_MENVCFGH);
|
||||
menvcfgh_val |= ENVCFGH_STCE;
|
||||
csr_write(CSR_MENVCFGH, menvcfgh_val);
|
||||
#else
|
||||
menvcfg_val |= ENVCFG_STCE;
|
||||
#endif
|
||||
}
|
||||
|
||||
csr_write(CSR_MENVCFG, menvcfg_val);
|
||||
}
|
||||
|
||||
/* Disable all interrupts */
|
||||
csr_write(CSR_MIE, 0);
|
||||
|
||||
@@ -108,7 +208,7 @@ static int delegate_traps(struct sbi_scratch *scratch)
|
||||
|
||||
/* Send M-mode interrupts and most exceptions to S-mode */
|
||||
interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
interrupts |= MIP_LCOFIP;
|
||||
|
||||
exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
|
||||
@@ -234,103 +334,156 @@ int sbi_hart_pmp_configure(struct sbi_scratch *scratch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a particular hart feature is available
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param feature the feature to check
|
||||
* @returns true (feature available) or false (feature not available)
|
||||
*/
|
||||
bool sbi_hart_has_feature(struct sbi_scratch *scratch, unsigned long feature)
|
||||
int sbi_hart_priv_version(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
if (hfeatures->features & feature)
|
||||
return hfeatures->priv_version;
|
||||
}
|
||||
|
||||
void sbi_hart_get_priv_version_str(struct sbi_scratch *scratch,
|
||||
char *version_str, int nvstr)
|
||||
{
|
||||
char *temp;
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
switch (hfeatures->priv_version) {
|
||||
case SBI_HART_PRIV_VER_1_10:
|
||||
temp = "v1.10";
|
||||
break;
|
||||
case SBI_HART_PRIV_VER_1_11:
|
||||
temp = "v1.11";
|
||||
break;
|
||||
case SBI_HART_PRIV_VER_1_12:
|
||||
temp = "v1.12";
|
||||
break;
|
||||
default:
|
||||
temp = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
sbi_snprintf(version_str, nvstr, "%s", temp);
|
||||
}
|
||||
|
||||
static inline void __sbi_hart_update_extension(
|
||||
struct hart_features *hfeatures,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable)
|
||||
{
|
||||
if (enable)
|
||||
hfeatures->extensions |= BIT(ext);
|
||||
else
|
||||
hfeatures->extensions &= ~BIT(ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable a particular hart extension
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param ext the extension number to check
|
||||
* @param enable new state of hart extension
|
||||
*/
|
||||
void sbi_hart_update_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext,
|
||||
bool enable)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
__sbi_hart_update_extension(hfeatures, ext, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a particular hart extension is available
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param ext the extension number to check
|
||||
* @returns true (available) or false (not available)
|
||||
*/
|
||||
bool sbi_hart_has_extension(struct sbi_scratch *scratch,
|
||||
enum sbi_hart_extensions ext)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
|
||||
if (hfeatures->extensions & BIT(ext))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned long hart_get_features(struct sbi_scratch *scratch)
|
||||
static inline char *sbi_hart_extension_id2string(int ext)
|
||||
{
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
char *estr = NULL;
|
||||
|
||||
return hfeatures->features;
|
||||
}
|
||||
|
||||
static inline char *sbi_hart_feature_id2string(unsigned long feature)
|
||||
{
|
||||
char *fstr = NULL;
|
||||
|
||||
if (!feature)
|
||||
return NULL;
|
||||
|
||||
switch (feature) {
|
||||
case SBI_HART_HAS_SCOUNTEREN:
|
||||
fstr = "scounteren";
|
||||
switch (ext) {
|
||||
case SBI_HART_EXT_SSCOFPMF:
|
||||
estr = "sscofpmf";
|
||||
break;
|
||||
case SBI_HART_HAS_MCOUNTEREN:
|
||||
fstr = "mcounteren";
|
||||
case SBI_HART_EXT_TIME:
|
||||
estr = "time";
|
||||
break;
|
||||
case SBI_HART_HAS_MCOUNTINHIBIT:
|
||||
fstr = "mcountinhibit";
|
||||
case SBI_HART_EXT_AIA:
|
||||
estr = "aia";
|
||||
break;
|
||||
case SBI_HART_HAS_SSCOFPMF:
|
||||
fstr = "sscofpmf";
|
||||
case SBI_HART_EXT_SSTC:
|
||||
estr = "sstc";
|
||||
break;
|
||||
case SBI_HART_HAS_TIME:
|
||||
fstr = "time";
|
||||
case SBI_HART_EXT_SMSTATEEN:
|
||||
estr = "smstateen";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return fstr;
|
||||
return estr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hart features in string format
|
||||
* Get the hart extensions in string format
|
||||
*
|
||||
* @param scratch pointer to the HART scratch space
|
||||
* @param features_str pointer to a char array where the features string will be
|
||||
* updated
|
||||
* @param nfstr length of the features_str. The feature string will be truncated
|
||||
* if nfstr is not long enough.
|
||||
* @param extensions_str pointer to a char array where the extensions string
|
||||
* will be updated
|
||||
* @param nestr length of the features_str. The feature string will be
|
||||
* truncated if nestr is not long enough.
|
||||
*/
|
||||
void sbi_hart_get_features_str(struct sbi_scratch *scratch,
|
||||
char *features_str, int nfstr)
|
||||
void sbi_hart_get_extensions_str(struct sbi_scratch *scratch,
|
||||
char *extensions_str, int nestr)
|
||||
{
|
||||
unsigned long features, feat = 1UL;
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
int offset = 0, ext = 0;
|
||||
char *temp;
|
||||
int offset = 0;
|
||||
|
||||
if (!features_str || nfstr <= 0)
|
||||
if (!extensions_str || nestr <= 0)
|
||||
return;
|
||||
sbi_memset(features_str, 0, nfstr);
|
||||
sbi_memset(extensions_str, 0, nestr);
|
||||
|
||||
features = hart_get_features(scratch);
|
||||
if (!features)
|
||||
if (!hfeatures->extensions)
|
||||
goto done;
|
||||
|
||||
do {
|
||||
if (features & feat) {
|
||||
temp = sbi_hart_feature_id2string(feat);
|
||||
if (hfeatures->extensions & BIT(ext)) {
|
||||
temp = sbi_hart_extension_id2string(ext);
|
||||
if (temp) {
|
||||
sbi_snprintf(features_str + offset, nfstr,
|
||||
sbi_snprintf(extensions_str + offset,
|
||||
nestr - offset,
|
||||
"%s,", temp);
|
||||
offset = offset + sbi_strlen(temp) + 1;
|
||||
}
|
||||
}
|
||||
feat = feat << 1;
|
||||
} while (feat <= SBI_HART_HAS_LAST_FEATURE);
|
||||
|
||||
ext++;
|
||||
} while (ext < SBI_HART_EXT_MAX);
|
||||
|
||||
done:
|
||||
if (offset)
|
||||
features_str[offset - 1] = '\0';
|
||||
extensions_str[offset - 1] = '\0';
|
||||
else
|
||||
sbi_strncpy(features_str, "none", nfstr);
|
||||
sbi_strncpy(extensions_str, "none", nestr);
|
||||
}
|
||||
|
||||
static unsigned long hart_pmp_get_allowed_addr(void)
|
||||
@@ -368,7 +521,7 @@ static int hart_pmu_get_allowed_bits(void)
|
||||
if (trap.cause)
|
||||
return 0;
|
||||
}
|
||||
num_bits = __fls(val) + 1;
|
||||
num_bits = sbi_fls(val) + 1;
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_allowed(CSR_MHPMCOUNTER3H, (ulong)&trap, val);
|
||||
if (!trap.cause) {
|
||||
@@ -376,34 +529,39 @@ static int hart_pmu_get_allowed_bits(void)
|
||||
if (trap.cause)
|
||||
return num_bits;
|
||||
}
|
||||
num_bits += __fls(val) + 1;
|
||||
num_bits += sbi_fls(val) + 1;
|
||||
|
||||
#endif
|
||||
|
||||
return num_bits;
|
||||
}
|
||||
|
||||
static void hart_detect_features(struct sbi_scratch *scratch)
|
||||
static int hart_detect_features(struct sbi_scratch *scratch)
|
||||
{
|
||||
struct sbi_trap_info trap = {0};
|
||||
struct hart_features *hfeatures;
|
||||
unsigned long val;
|
||||
struct hart_features *hfeatures =
|
||||
sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
unsigned long val, oldval;
|
||||
int rc;
|
||||
|
||||
/* Reset hart features */
|
||||
hfeatures = sbi_scratch_offset_ptr(scratch, hart_features_offset);
|
||||
hfeatures->features = 0;
|
||||
/* If hart features already detected then do nothing */
|
||||
if (hfeatures->detected)
|
||||
return 0;
|
||||
|
||||
/* Clear hart features */
|
||||
hfeatures->extensions = 0;
|
||||
hfeatures->pmp_count = 0;
|
||||
hfeatures->mhpm_count = 0;
|
||||
|
||||
#define __check_csr(__csr, __rdonly, __wrval, __field, __skip) \
|
||||
val = csr_read_allowed(__csr, (ulong)&trap); \
|
||||
oldval = csr_read_allowed(__csr, (ulong)&trap); \
|
||||
if (!trap.cause) { \
|
||||
if (__rdonly) { \
|
||||
(hfeatures->__field)++; \
|
||||
} else { \
|
||||
csr_write_allowed(__csr, (ulong)&trap, __wrval);\
|
||||
if (!trap.cause) { \
|
||||
if (csr_swap(__csr, val) == __wrval) \
|
||||
if (csr_swap(__csr, oldval) == __wrval) \
|
||||
(hfeatures->__field)++; \
|
||||
else \
|
||||
goto __skip; \
|
||||
@@ -439,8 +597,8 @@ static void hart_detect_features(struct sbi_scratch *scratch)
|
||||
*/
|
||||
val = hart_pmp_get_allowed_addr();
|
||||
if (val) {
|
||||
hfeatures->pmp_gran = 1 << (__ffs(val) + 2);
|
||||
hfeatures->pmp_addr_bits = __fls(val) + 1;
|
||||
hfeatures->pmp_gran = 1 << (sbi_ffs(val) + 2);
|
||||
hfeatures->pmp_addr_bits = sbi_fls(val) + 1;
|
||||
/* Detect number of PMP regions. At least PMPADDR0 should be implemented*/
|
||||
__check_csr_64(CSR_PMPADDR0, 0, val, pmp_count, __pmp_skip);
|
||||
}
|
||||
@@ -469,43 +627,69 @@ __mhpm_skip:
|
||||
#undef __check_csr_2
|
||||
#undef __check_csr
|
||||
|
||||
/* Detect if hart supports SCOUNTEREN feature */
|
||||
val = csr_read_allowed(CSR_SCOUNTEREN, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_SCOUNTEREN, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_SCOUNTEREN;
|
||||
}
|
||||
|
||||
/* Detect if hart supports MCOUNTEREN feature */
|
||||
/* Detect if hart supports Priv v1.10 */
|
||||
val = csr_read_allowed(CSR_MCOUNTEREN, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_MCOUNTEREN, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_MCOUNTEREN;
|
||||
}
|
||||
if (!trap.cause)
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_10;
|
||||
|
||||
/* Detect if hart supports MCOUNTINHIBIT feature */
|
||||
/* Detect if hart supports Priv v1.11 */
|
||||
val = csr_read_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap);
|
||||
if (!trap.cause) {
|
||||
csr_write_allowed(CSR_MCOUNTINHIBIT, (unsigned long)&trap, val);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_MCOUNTINHIBIT;
|
||||
}
|
||||
if (!trap.cause &&
|
||||
(hfeatures->priv_version >= SBI_HART_PRIV_VER_1_10))
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_11;
|
||||
|
||||
/* Detect if hart supports Priv v1.12 */
|
||||
csr_read_allowed(CSR_MENVCFG, (unsigned long)&trap);
|
||||
if (!trap.cause &&
|
||||
(hfeatures->priv_version >= SBI_HART_PRIV_VER_1_11))
|
||||
hfeatures->priv_version = SBI_HART_PRIV_VER_1_12;
|
||||
|
||||
/* Counter overflow/filtering is not useful without mcounter/inhibit */
|
||||
if (hfeatures->features & SBI_HART_HAS_MCOUNTINHIBIT &&
|
||||
hfeatures->features & SBI_HART_HAS_MCOUNTEREN) {
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
/* Detect if hart supports sscofpmf */
|
||||
csr_read_allowed(CSR_SCOUNTOVF, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_SSCOFPMF;
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SSCOFPMF, true);
|
||||
}
|
||||
|
||||
/* Detect if hart supports time CSR */
|
||||
csr_read_allowed(CSR_TIME, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
hfeatures->features |= SBI_HART_HAS_TIME;
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_TIME, true);
|
||||
|
||||
/* Detect if hart has AIA local interrupt CSRs */
|
||||
csr_read_allowed(CSR_MTOPI, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_AIA, true);
|
||||
|
||||
/* Detect if hart supports stimecmp CSR(Sstc extension) */
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
csr_read_allowed(CSR_STIMECMP, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SSTC, true);
|
||||
}
|
||||
|
||||
/* Detect if hart supports mstateen CSRs */
|
||||
if (hfeatures->priv_version >= SBI_HART_PRIV_VER_1_12) {
|
||||
val = csr_read_allowed(CSR_MSTATEEN0, (unsigned long)&trap);
|
||||
if (!trap.cause)
|
||||
__sbi_hart_update_extension(hfeatures,
|
||||
SBI_HART_EXT_SMSTATEEN, true);
|
||||
}
|
||||
|
||||
/* Let platform populate extensions */
|
||||
rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr());
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Mark hart feature detection done */
|
||||
hfeatures->detected = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sbi_hart_reinit(struct sbi_scratch *scratch)
|
||||
@@ -527,6 +711,8 @@ int sbi_hart_reinit(struct sbi_scratch *scratch)
|
||||
|
||||
int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (cold_boot) {
|
||||
if (misa_extension('H'))
|
||||
sbi_hart_expected_trap = &__sbi_expected_trap_hext;
|
||||
@@ -537,7 +723,9 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
return SBI_ENOMEM;
|
||||
}
|
||||
|
||||
hart_detect_features(scratch);
|
||||
rc = hart_detect_features(scratch);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return sbi_hart_reinit(scratch);
|
||||
}
|
||||
|
@@ -113,8 +113,8 @@ static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Save MIE CSR */
|
||||
saved_mie = csr_read(CSR_MIE);
|
||||
|
||||
/* Set MSIE bit to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
/* Set MSIE and MEIE bits to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
|
||||
|
||||
/* Wait for hart_add call*/
|
||||
while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) {
|
||||
@@ -171,13 +171,19 @@ static int hsm_device_hart_stop(void)
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
static int hsm_device_hart_suspend(u32 suspend_type, ulong raddr)
|
||||
static int hsm_device_hart_suspend(u32 suspend_type)
|
||||
{
|
||||
if (hsm_dev && hsm_dev->hart_suspend)
|
||||
return hsm_dev->hart_suspend(suspend_type, raddr);
|
||||
return hsm_dev->hart_suspend(suspend_type);
|
||||
return SBI_ENOTSUPP;
|
||||
}
|
||||
|
||||
static void hsm_device_hart_resume(void)
|
||||
{
|
||||
if (hsm_dev && hsm_dev->hart_resume)
|
||||
hsm_dev->hart_resume();
|
||||
}
|
||||
|
||||
int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
|
||||
{
|
||||
u32 i;
|
||||
@@ -313,7 +319,7 @@ int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sbi_hsm_suspend_ret_default(struct sbi_scratch *scratch)
|
||||
static int __sbi_hsm_suspend_default(struct sbi_scratch *scratch)
|
||||
{
|
||||
/* Wait for interrupt */
|
||||
wfi();
|
||||
@@ -353,23 +359,6 @@ static void __sbi_hsm_suspend_non_ret_restore(struct sbi_scratch *scratch)
|
||||
csr_write(CSR_MIP, (hdata->saved_mip & (MIP_SSIP | MIP_STIP)));
|
||||
}
|
||||
|
||||
static int __sbi_hsm_suspend_non_ret_default(struct sbi_scratch *scratch,
|
||||
ulong raddr)
|
||||
{
|
||||
void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
|
||||
|
||||
/* Wait for interrupt */
|
||||
wfi();
|
||||
|
||||
/*
|
||||
* Directly jump to warm reboot to simulate resume from a
|
||||
* non-retentive suspend.
|
||||
*/
|
||||
jump_warmboot();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch)
|
||||
{
|
||||
int oldstate;
|
||||
@@ -384,6 +373,8 @@ void sbi_hsm_hart_resume_start(struct sbi_scratch *scratch)
|
||||
__func__, oldstate);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
|
||||
hsm_device_hart_resume();
|
||||
}
|
||||
|
||||
void sbi_hsm_hart_resume_finish(struct sbi_scratch *scratch)
|
||||
@@ -465,17 +456,28 @@ int sbi_hsm_hart_suspend(struct sbi_scratch *scratch, u32 suspend_type,
|
||||
__sbi_hsm_suspend_non_ret_save(scratch);
|
||||
|
||||
/* Try platform specific suspend */
|
||||
ret = hsm_device_hart_suspend(suspend_type, scratch->warmboot_addr);
|
||||
ret = hsm_device_hart_suspend(suspend_type);
|
||||
if (ret == SBI_ENOTSUPP) {
|
||||
/* Try generic implementation of default suspend types */
|
||||
if (suspend_type == SBI_HSM_SUSPEND_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_ret_default(scratch);
|
||||
} else if (suspend_type == SBI_HSM_SUSPEND_NON_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_non_ret_default(scratch,
|
||||
scratch->warmboot_addr);
|
||||
if (suspend_type == SBI_HSM_SUSPEND_RET_DEFAULT ||
|
||||
suspend_type == SBI_HSM_SUSPEND_NON_RET_DEFAULT) {
|
||||
ret = __sbi_hsm_suspend_default(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The platform may have coordinated a retentive suspend, or it may
|
||||
* have exited early from a non-retentive suspend. Either way, the
|
||||
* caller is not expecting a successful return, so jump to the warm
|
||||
* boot entry point to simulate resume from a non-retentive suspend.
|
||||
*/
|
||||
if (ret == 0 && (suspend_type & SBI_HSM_SUSP_NON_RET_BIT)) {
|
||||
void (*jump_warmboot)(void) =
|
||||
(void (*)(void))scratch->warmboot_addr;
|
||||
|
||||
jump_warmboot();
|
||||
}
|
||||
|
||||
fail_restore_state:
|
||||
/*
|
||||
* We might have successfully resumed from retentive suspend
|
||||
|
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_barrier.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/sbi_bitops.h>
|
||||
#include <sbi/sbi_emulate_csr.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
#include <sbi/sbi_unpriv.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
typedef int (*illegal_insn_func)(ulong insn, struct sbi_trap_regs *regs);
|
||||
|
||||
@@ -32,13 +34,31 @@ static int truly_illegal_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
return sbi_trap_redirect(regs, &trap);
|
||||
}
|
||||
|
||||
static int misc_mem_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
{
|
||||
/* Errata workaround: emulate `fence.tso` as `fence rw, rw`. */
|
||||
if ((insn & INSN_MASK_FENCE_TSO) == INSN_MATCH_FENCE_TSO) {
|
||||
smp_mb();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return truly_illegal_insn(insn, regs);
|
||||
}
|
||||
|
||||
static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
{
|
||||
int do_write, rs1_num = (insn >> 15) & 0x1f;
|
||||
ulong rs1_val = GET_RS1(insn, regs);
|
||||
int csr_num = (u32)insn >> 20;
|
||||
ulong prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
|
||||
ulong csr_val, new_csr_val;
|
||||
|
||||
if (prev_mode == PRV_M) {
|
||||
sbi_printf("%s: Failed to access CSR %#x from M-mode",
|
||||
__func__, csr_num);
|
||||
return SBI_EFAIL;
|
||||
}
|
||||
|
||||
/* TODO: Ensure that we got CSR read/write instruction */
|
||||
|
||||
if (sbi_emulate_csr_read(csr_num, regs, &csr_val))
|
||||
@@ -80,11 +100,11 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static illegal_insn_func illegal_insn_table[32] = {
|
||||
static const illegal_insn_func illegal_insn_table[32] = {
|
||||
truly_illegal_insn, /* 0 */
|
||||
truly_illegal_insn, /* 1 */
|
||||
truly_illegal_insn, /* 2 */
|
||||
truly_illegal_insn, /* 3 */
|
||||
misc_mem_opcode_insn, /* 3 */
|
||||
truly_illegal_insn, /* 4 */
|
||||
truly_illegal_insn, /* 5 */
|
||||
truly_illegal_insn, /* 6 */
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_hsm.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
@@ -138,10 +139,12 @@ static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Boot HART details */
|
||||
sbi_printf("Boot HART ID : %u\n", hartid);
|
||||
sbi_printf("Boot HART Domain : %s\n", dom->name);
|
||||
sbi_hart_get_priv_version_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART Priv Version : %s\n", str);
|
||||
misa_string(xlen, str, sizeof(str));
|
||||
sbi_printf("Boot HART ISA : %s\n", str);
|
||||
sbi_hart_get_features_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART Features : %s\n", str);
|
||||
sbi_printf("Boot HART Base ISA : %s\n", str);
|
||||
sbi_hart_get_extensions_str(scratch, str, sizeof(str));
|
||||
sbi_printf("Boot HART ISA Extensions : %s\n", str);
|
||||
sbi_printf("Boot HART PMP Count : %d\n",
|
||||
sbi_hart_pmp_count(scratch));
|
||||
sbi_printf("Boot HART PMP Granularity : %lu\n",
|
||||
@@ -165,8 +168,8 @@ static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
/* Save MIE CSR */
|
||||
saved_mie = csr_read(CSR_MIE);
|
||||
|
||||
/* Set MSIE bit to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP);
|
||||
/* Set MSIE and MEIE bits to receive IPI */
|
||||
csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
|
||||
|
||||
/* Acquire coldboot lock */
|
||||
spin_lock(&coldboot_lock);
|
||||
@@ -182,7 +185,7 @@ static void wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
do {
|
||||
wfi();
|
||||
cmip = csr_read(CSR_MIP);
|
||||
} while (!(cmip & MIP_MSIP));
|
||||
} while (!(cmip & (MIP_MSIP | MIP_MEIP)));
|
||||
};
|
||||
|
||||
/* Acquire coldboot lock */
|
||||
@@ -270,9 +273,9 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
|
||||
|
||||
sbi_boot_print_banner(scratch);
|
||||
|
||||
rc = sbi_platform_irqchip_init(plat, TRUE);
|
||||
rc = sbi_irqchip_init(scratch, TRUE);
|
||||
if (rc) {
|
||||
sbi_printf("%s: platform irqchip init failed (error %d)\n",
|
||||
sbi_printf("%s: irqchip init failed (error %d)\n",
|
||||
__func__, rc);
|
||||
sbi_hart_hang();
|
||||
}
|
||||
@@ -373,7 +376,7 @@ static void init_warm_startup(struct sbi_scratch *scratch, u32 hartid)
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
rc = sbi_platform_irqchip_init(plat, FALSE);
|
||||
rc = sbi_irqchip_init(scratch, FALSE);
|
||||
if (rc)
|
||||
sbi_hart_hang();
|
||||
|
||||
@@ -494,6 +497,14 @@ void __noreturn sbi_init(struct sbi_scratch *scratch)
|
||||
if (next_mode_supported && atomic_xchg(&coldboot_lottery, 1) == 0)
|
||||
coldboot = TRUE;
|
||||
|
||||
/*
|
||||
* Do platform specific nascent (very early) initialization so
|
||||
* that platform can initialize platform specific per-HART CSRs
|
||||
* or per-HART devices.
|
||||
*/
|
||||
if (sbi_platform_nascent_init(plat))
|
||||
sbi_hart_hang();
|
||||
|
||||
if (coldboot)
|
||||
init_coldboot(scratch, hartid);
|
||||
else
|
||||
@@ -542,7 +553,7 @@ void __noreturn sbi_exit(struct sbi_scratch *scratch)
|
||||
|
||||
sbi_ipi_exit(scratch);
|
||||
|
||||
sbi_platform_irqchip_exit(plat);
|
||||
sbi_irqchip_exit(scratch);
|
||||
|
||||
sbi_platform_final_exit(plat);
|
||||
|
||||
|
54
lib/sbi/sbi_irqchip.c
Normal file
54
lib/sbi/sbi_irqchip.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <apatel@ventanamicro.com>
|
||||
*/
|
||||
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_platform.h>
|
||||
|
||||
static int default_irqfn(struct sbi_trap_regs *regs)
|
||||
{
|
||||
return SBI_ENODEV;
|
||||
}
|
||||
|
||||
static int (*ext_irqfn)(struct sbi_trap_regs *regs) = default_irqfn;
|
||||
|
||||
void sbi_irqchip_set_irqfn(int (*fn)(struct sbi_trap_regs *regs))
|
||||
{
|
||||
if (fn)
|
||||
ext_irqfn = fn;
|
||||
}
|
||||
|
||||
int sbi_irqchip_process(struct sbi_trap_regs *regs)
|
||||
{
|
||||
return ext_irqfn(regs);
|
||||
}
|
||||
|
||||
int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
{
|
||||
int rc;
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
rc = sbi_platform_irqchip_init(plat, cold_boot);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (ext_irqfn != default_irqfn)
|
||||
csr_set(CSR_MIE, MIP_MEIP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sbi_irqchip_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
|
||||
|
||||
if (ext_irqfn != default_irqfn)
|
||||
csr_clear(CSR_MIE, MIP_MEIP);
|
||||
|
||||
sbi_platform_irqchip_exit(plat);
|
||||
}
|
@@ -22,6 +22,18 @@ union reg_data {
|
||||
u64 data_u64;
|
||||
};
|
||||
|
||||
static ulong sbi_misaligned_tinst_fixup(ulong orig_tinst, ulong new_tinst,
|
||||
ulong addr_offset)
|
||||
{
|
||||
if (new_tinst == INSN_PSEUDO_VS_LOAD ||
|
||||
new_tinst == INSN_PSEUDO_VS_STORE)
|
||||
return new_tinst;
|
||||
else if (orig_tinst == 0)
|
||||
return 0UL;
|
||||
else
|
||||
return orig_tinst | (addr_offset << SH_RS1);
|
||||
}
|
||||
|
||||
int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
struct sbi_trap_regs *regs)
|
||||
{
|
||||
@@ -126,6 +138,8 @@ int sbi_misaligned_load_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
&uptrap);
|
||||
if (uptrap.cause) {
|
||||
uptrap.epc = regs->mepc;
|
||||
uptrap.tinst = sbi_misaligned_tinst_fixup(
|
||||
tinst, uptrap.tinst, i);
|
||||
return sbi_trap_redirect(regs, &uptrap);
|
||||
}
|
||||
}
|
||||
@@ -238,6 +252,8 @@ int sbi_misaligned_store_handler(ulong addr, ulong tval2, ulong tinst,
|
||||
&uptrap);
|
||||
if (uptrap.cause) {
|
||||
uptrap.epc = regs->mepc;
|
||||
uptrap.tinst = sbi_misaligned_tinst_fixup(
|
||||
tinst, uptrap.tinst, i);
|
||||
return sbi_trap_redirect(regs, &uptrap);
|
||||
}
|
||||
}
|
||||
|
@@ -181,6 +181,9 @@ static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap,
|
||||
int i = 0;
|
||||
bool is_overlap;
|
||||
struct sbi_pmu_hw_event *event = &hw_event_map[num_hw_events];
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
int hw_ctr_avail = sbi_hart_mhpm_count(scratch);
|
||||
uint32_t ctr_avail_mask = ((uint32_t)(~0) >> (32 - (hw_ctr_avail + 3)));
|
||||
|
||||
/* The first two counters are reserved by priv spec */
|
||||
if (eidx_start > SBI_PMU_HW_INSTRUCTIONS && (cmap & SBI_PMU_FIXED_CTR_MASK))
|
||||
@@ -208,7 +211,8 @@ static int pmu_add_hw_event_map(u32 eidx_start, u32 eidx_end, u32 cmap,
|
||||
}
|
||||
|
||||
event->select_mask = select_mask;
|
||||
event->counters = cmap;
|
||||
/* Map the only the counters that are available in the hardware */
|
||||
event->counters = cmap & ctr_avail_mask;
|
||||
event->select = select;
|
||||
num_hw_events++;
|
||||
|
||||
@@ -252,7 +256,7 @@ static int pmu_ctr_enable_irq_hw(int ctr_idx)
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
mhpmevent_csr = CSR_MHPMEVENT3H + ctr_idx - 3;
|
||||
of_mask = ~MHPMEVENTH_OF;
|
||||
of_mask = (uint32_t)~MHPMEVENTH_OF;
|
||||
#else
|
||||
mhpmevent_csr = CSR_MHPMEVENT3 + ctr_idx - 3;
|
||||
of_mask = ~MHPMEVENT_OF;
|
||||
@@ -293,10 +297,10 @@ static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
|
||||
unsigned long mctr_inhbt;
|
||||
|
||||
/* Make sure the counter index lies within the range and is not TM bit */
|
||||
if (cidx > num_hw_ctrs || cidx == 1)
|
||||
if (cidx >= num_hw_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11)
|
||||
goto skip_inhibit_update;
|
||||
|
||||
/*
|
||||
@@ -309,7 +313,7 @@ static int pmu_ctr_start_hw(uint32_t cidx, uint64_t ival, bool ival_update)
|
||||
|
||||
__clear_bit(cidx, &mctr_inhbt);
|
||||
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
pmu_ctr_enable_irq_hw(cidx);
|
||||
csr_write(CSR_MCOUNTINHIBIT, mctr_inhbt);
|
||||
|
||||
@@ -343,7 +347,7 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
|
||||
int ret = SBI_EINVAL;
|
||||
bool bUpdate = FALSE;
|
||||
|
||||
if (__fls(ctr_mask) >= total_ctrs)
|
||||
if (sbi_fls(ctr_mask) >= total_ctrs)
|
||||
return ret;
|
||||
|
||||
if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
|
||||
@@ -368,13 +372,13 @@ static int pmu_ctr_stop_hw(uint32_t cidx)
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
unsigned long mctr_inhbt;
|
||||
|
||||
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
if (sbi_hart_priv_version(scratch) < SBI_HART_PRIV_VER_1_11)
|
||||
return 0;
|
||||
|
||||
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
|
||||
|
||||
/* Make sure the counter index lies within the range and is not TM bit */
|
||||
if (cidx > num_hw_ctrs || cidx == 1)
|
||||
if (cidx >= num_hw_ctrs || cidx == 1)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (!__test_bit(cidx, &mctr_inhbt)) {
|
||||
@@ -400,7 +404,9 @@ static int pmu_reset_hw_mhpmevent(int ctr_idx)
|
||||
return SBI_EFAIL;
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, 0);
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(),
|
||||
SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, 0);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, 0);
|
||||
#endif
|
||||
@@ -417,7 +423,7 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
|
||||
uint32_t event_code;
|
||||
unsigned long ctr_mask = cmask << cbase;
|
||||
|
||||
if (__fls(ctr_mask) >= total_ctrs)
|
||||
if (sbi_fls(ctr_mask) >= total_ctrs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
|
||||
@@ -466,16 +472,22 @@ static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx,
|
||||
if (!mhpmevent_val || ctr_idx < 3 || ctr_idx >= SBI_PMU_HW_CTR_MAX)
|
||||
return SBI_EFAIL;
|
||||
|
||||
/* Always clear the OVF bit and inhibit countin of events in M-mode */
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) | MHPMEVENT_MINH;
|
||||
/**
|
||||
* Always set the OVF bit(disable interrupts) and inhibit counting of
|
||||
* events in M-mode. The OVF bit should be enabled during the start call.
|
||||
*/
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) |
|
||||
MHPMEVENT_MINH | MHPMEVENT_OF;
|
||||
|
||||
/* Update the inhibit flags based on inhibit flags received from supervisor */
|
||||
pmu_update_inhibit_flags(flags, &mhpmevent_val);
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val & 0xFFFFFFFF);
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3, mhpmevent_val >> BITS_PER_LONG);
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
csr_write_num(CSR_MHPMEVENT3H + ctr_idx - 3,
|
||||
mhpmevent_val >> BITS_PER_LONG);
|
||||
#else
|
||||
csr_write_num(CSR_MHPMEVENT3 + ctr_idx - 3, mhpmevent_val);
|
||||
#endif
|
||||
@@ -504,7 +516,7 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo
|
||||
u32 hartid = current_hartid();
|
||||
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
|
||||
|
||||
if (cbase > num_hw_ctrs)
|
||||
if (cbase >= num_hw_ctrs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/**
|
||||
@@ -513,10 +525,10 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo
|
||||
*/
|
||||
fixed_ctr = pmu_ctr_find_fixed_fw(event_idx);
|
||||
if (fixed_ctr >= 0 &&
|
||||
!sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
return fixed_ctr;
|
||||
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
mctr_inhbt = csr_read(CSR_MCOUNTINHIBIT);
|
||||
for (i = 0; i < num_hw_events; i++) {
|
||||
temp = &hw_event_map[i];
|
||||
@@ -543,7 +555,7 @@ static int pmu_ctr_find_hw(unsigned long cbase, unsigned long cmask, unsigned lo
|
||||
if (active_events[hartid][cbase] != SBI_PMU_EVENT_IDX_INVALID)
|
||||
continue;
|
||||
/* If mcountinhibit is supported, the bit must be enabled */
|
||||
if ((sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT)) &&
|
||||
if ((sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) &&
|
||||
!__test_bit(cbase, &mctr_inhbt))
|
||||
continue;
|
||||
/* We found a valid counter that is not started yet */
|
||||
@@ -581,8 +593,8 @@ static int pmu_ctr_find_fw(unsigned long cbase, unsigned long cmask, u32 hartid)
|
||||
int fw_base;
|
||||
unsigned long ctr_mask = cmask << cbase;
|
||||
|
||||
if (cbase <= num_hw_ctrs)
|
||||
fw_base = num_hw_ctrs + 1;
|
||||
if (cbase < num_hw_ctrs)
|
||||
fw_base = num_hw_ctrs;
|
||||
else
|
||||
fw_base = cbase;
|
||||
|
||||
@@ -606,7 +618,7 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
|
||||
unsigned long tmp = cidx_mask << cidx_base;
|
||||
|
||||
/* Do a basic sanity check of counter base & mask */
|
||||
if (__fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
|
||||
if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
|
||||
@@ -682,7 +694,7 @@ int sbi_pmu_ctr_get_info(uint32_t cidx, unsigned long *ctr_info)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* We have 31 HW counters with 31 being the last index(MHPMCOUNTER31) */
|
||||
if (cidx <= num_hw_ctrs) {
|
||||
if (cidx < num_hw_ctrs) {
|
||||
cinfo.type = SBI_PMU_CTR_TYPE_HW;
|
||||
cinfo.csr = CSR_CYCLE + cidx;
|
||||
/* mcycle & minstret are always 64 bit */
|
||||
@@ -718,10 +730,11 @@ void sbi_pmu_exit(struct sbi_scratch *scratch)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_MCOUNTINHIBIT))
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11)
|
||||
csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8);
|
||||
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10)
|
||||
csr_write(CSR_MCOUNTEREN, -1);
|
||||
pmu_reset_event_map(hartid);
|
||||
}
|
||||
|
||||
@@ -736,7 +749,7 @@ int sbi_pmu_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
sbi_platform_pmu_init(plat);
|
||||
|
||||
/* mcycle & minstret is available always */
|
||||
num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 2;
|
||||
num_hw_ctrs = sbi_hart_mhpm_count(scratch) + 3;
|
||||
total_ctrs = num_hw_ctrs + SBI_PMU_FW_CTR_MAX;
|
||||
}
|
||||
|
||||
|
@@ -149,8 +149,8 @@ void *sbi_memmove(void *dest, const void *src, size_t count)
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
temp1 = dest + count - 1;
|
||||
temp2 = src + count - 1;
|
||||
temp1 = (char *)dest + count - 1;
|
||||
temp2 = (char *)src + count - 1;
|
||||
|
||||
while (count > 0) {
|
||||
*temp1-- = *temp2--;
|
||||
|
@@ -44,11 +44,6 @@ static u64 get_ticks(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static u64 get_platform_ticks(void)
|
||||
{
|
||||
return timer_dev->timer_value();
|
||||
}
|
||||
|
||||
static void nop_delay_fn(void *opaque)
|
||||
{
|
||||
cpu_relax();
|
||||
@@ -124,16 +119,35 @@ void sbi_timer_set_delta_upper(ulong delta_upper)
|
||||
void sbi_timer_event_start(u64 next_event)
|
||||
{
|
||||
sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SET_TIMER);
|
||||
if (timer_dev && timer_dev->timer_event_start)
|
||||
|
||||
/**
|
||||
* Update the stimecmp directly if available. This allows
|
||||
* the older software to leverage sstc extension on newer hardware.
|
||||
*/
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC)) {
|
||||
#if __riscv_xlen == 32
|
||||
csr_write(CSR_STIMECMP, next_event & 0xFFFFFFFF);
|
||||
csr_write(CSR_STIMECMPH, next_event >> 32);
|
||||
#else
|
||||
csr_write(CSR_STIMECMP, next_event);
|
||||
#endif
|
||||
} else if (timer_dev && timer_dev->timer_event_start) {
|
||||
timer_dev->timer_event_start(next_event);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
csr_clear(CSR_MIP, MIP_STIP);
|
||||
}
|
||||
csr_set(CSR_MIE, MIP_MTIP);
|
||||
}
|
||||
|
||||
void sbi_timer_process(void)
|
||||
{
|
||||
csr_clear(CSR_MIE, MIP_MTIP);
|
||||
csr_set(CSR_MIP, MIP_STIP);
|
||||
/*
|
||||
* If sstc extension is available, supervisor can receive the timer
|
||||
* directly without M-mode come in between. This function should
|
||||
* only invoked if M-mode programs the timer for its own purpose.
|
||||
*/
|
||||
if (!sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC))
|
||||
csr_set(CSR_MIP, MIP_STIP);
|
||||
}
|
||||
|
||||
const struct sbi_timer_device *sbi_timer_get_device(void)
|
||||
@@ -148,7 +162,7 @@ void sbi_timer_set_device(const struct sbi_timer_device *dev)
|
||||
|
||||
timer_dev = dev;
|
||||
if (!get_time_val && timer_dev->timer_value)
|
||||
get_time_val = get_platform_ticks;
|
||||
get_time_val = timer_dev->timer_value;
|
||||
}
|
||||
|
||||
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
@@ -161,7 +175,7 @@ int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
|
||||
if (!time_delta_off)
|
||||
return SBI_ENOMEM;
|
||||
|
||||
if (sbi_hart_has_feature(scratch, SBI_HART_HAS_TIME))
|
||||
if (sbi_hart_has_extension(scratch, SBI_HART_EXT_TIME))
|
||||
get_time_val = get_ticks;
|
||||
} else {
|
||||
if (!time_delta_off)
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <sbi/sbi_hart.h>
|
||||
#include <sbi/sbi_illegal_insn.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_misaligned_ldst.h>
|
||||
#include <sbi/sbi_pmu.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
@@ -98,17 +99,14 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
if (prev_mode != PRV_S && prev_mode != PRV_U)
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
/* For certain exceptions from VS/VU-mode we redirect to VS-mode */
|
||||
/* If exceptions came from VS/VU-mode, redirect to VS-mode if
|
||||
* delegated in hedeleg
|
||||
*/
|
||||
if (misa_extension('H') && prev_virt) {
|
||||
switch (trap->cause) {
|
||||
case CAUSE_FETCH_PAGE_FAULT:
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
case CAUSE_STORE_PAGE_FAULT:
|
||||
if ((trap->cause < __riscv_xlen) &&
|
||||
(csr_read(CSR_HEDELEG) & BIT(trap->cause))) {
|
||||
next_virt = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* Update MSTATUS MPV bits */
|
||||
@@ -195,6 +193,52 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_trap_nonaia_irq(struct sbi_trap_regs *regs, ulong mcause)
|
||||
{
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
case IRQ_M_EXT:
|
||||
return sbi_irqchip_process(regs);
|
||||
default:
|
||||
return SBI_ENOENT;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbi_trap_aia_irq(struct sbi_trap_regs *regs, ulong mcause)
|
||||
{
|
||||
int rc;
|
||||
unsigned long mtopi;
|
||||
|
||||
while ((mtopi = csr_read(CSR_MTOPI))) {
|
||||
mtopi = mtopi >> TOPI_IID_SHIFT;
|
||||
switch (mtopi) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
case IRQ_M_EXT:
|
||||
rc = sbi_irqchip_process(regs);
|
||||
if (rc)
|
||||
return rc;
|
||||
break;
|
||||
default:
|
||||
return SBI_ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle trap/interrupt
|
||||
*
|
||||
@@ -225,18 +269,15 @@ struct sbi_trap_regs *sbi_trap_handler(struct sbi_trap_regs *regs)
|
||||
}
|
||||
|
||||
if (mcause & (1UL << (__riscv_xlen - 1))) {
|
||||
mcause &= ~(1UL << (__riscv_xlen - 1));
|
||||
switch (mcause) {
|
||||
case IRQ_M_TIMER:
|
||||
sbi_timer_process();
|
||||
break;
|
||||
case IRQ_M_SOFT:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
default:
|
||||
msg = "unhandled external interrupt";
|
||||
if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(),
|
||||
SBI_HART_EXT_AIA))
|
||||
rc = sbi_trap_aia_irq(regs, mcause);
|
||||
else
|
||||
rc = sbi_trap_nonaia_irq(regs, mcause);
|
||||
if (rc) {
|
||||
msg = "unhandled local interrupt";
|
||||
goto trap_error;
|
||||
};
|
||||
}
|
||||
return regs;
|
||||
}
|
||||
|
||||
|
@@ -83,7 +83,7 @@ DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld)
|
||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld)
|
||||
#else
|
||||
#elif __riscv_xlen == 32
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw)
|
||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw)
|
||||
|
||||
@@ -112,6 +112,8 @@ void sbi_store_u64(u64 *addr, u64 val,
|
||||
if (trap->cause)
|
||||
return;
|
||||
}
|
||||
#else
|
||||
# error "Unexpected __riscv_xlen"
|
||||
#endif
|
||||
|
||||
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
||||
@@ -147,15 +149,17 @@ ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
||||
switch (trap->cause) {
|
||||
case CAUSE_LOAD_ACCESS:
|
||||
trap->cause = CAUSE_FETCH_ACCESS;
|
||||
trap->tval = mepc;
|
||||
trap->tinst = 0UL;
|
||||
break;
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
trap->cause = CAUSE_FETCH_PAGE_FAULT;
|
||||
trap->tval = mepc;
|
||||
trap->tinst = 0UL;
|
||||
break;
|
||||
case CAUSE_LOAD_GUEST_PAGE_FAULT:
|
||||
trap->cause = CAUSE_FETCH_GUEST_PAGE_FAULT;
|
||||
trap->tval = mepc;
|
||||
if (trap->tinst != INSN_PSEUDO_VS_LOAD &&
|
||||
trap->tinst != INSN_PSEUDO_VS_STORE)
|
||||
trap->tinst = 0UL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@@ -165,6 +165,9 @@ void fdt_domain_fixup(void *fdt)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, doffset))
|
||||
continue;
|
||||
|
||||
fdt_nop_property(fdt, doffset, "opensbi-domain");
|
||||
}
|
||||
|
||||
@@ -308,6 +311,9 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
sbi_hartmask_set_hart(val32, mask);
|
||||
}
|
||||
}
|
||||
@@ -347,7 +353,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (val && len >= 4) {
|
||||
cpu_offset = fdt_node_offset_by_phandle(fdt,
|
||||
fdt32_to_cpu(*val));
|
||||
if (cpu_offset >= 0)
|
||||
if (cpu_offset >= 0 && fdt_node_is_enabled(fdt, cpu_offset))
|
||||
fdt_parse_hart_id(fdt, cpu_offset, &val32);
|
||||
} else {
|
||||
if (domain_offset == *cold_domain_offset)
|
||||
@@ -414,6 +420,9 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque)
|
||||
if (SBI_HARTMASK_MAX_BITS <= val32)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
|
||||
if (!val || len < 4)
|
||||
return SBI_EINVAL;
|
||||
@@ -460,6 +469,9 @@ int fdt_domains_populate(void *fdt)
|
||||
if (hartid != cold_hartid)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
val = fdt_getprop(fdt, cpu_offset, "opensbi-domain", &len);
|
||||
if (val && len >= 4)
|
||||
cold_domain_offset = fdt_node_offset_by_phandle(fdt,
|
||||
|
@@ -38,6 +38,9 @@ void fdt_cpu_fixup(void *fdt)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Disable a HART DT node if one of the following is true:
|
||||
* 1. The HART is not assigned to the current domain
|
||||
@@ -52,6 +55,43 @@ void fdt_cpu_fixup(void *fdt)
|
||||
}
|
||||
}
|
||||
|
||||
static void fdt_domain_based_fixup_one(void *fdt, int nodeoff)
|
||||
{
|
||||
int rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
struct sbi_domain *dom = sbi_domain_thishart_ptr();
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return;
|
||||
|
||||
if (!sbi_domain_check_addr(dom, reg_addr, dom->next_mode,
|
||||
SBI_DOMAIN_READ | SBI_DOMAIN_WRITE)) {
|
||||
rc = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 32);
|
||||
if (rc < 0)
|
||||
return;
|
||||
fdt_setprop_string(fdt, nodeoff, "status", "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
void fdt_aplic_fixup(void *fdt)
|
||||
{
|
||||
int noff = 0;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,aplic")) >= 0)
|
||||
fdt_domain_based_fixup_one(fdt, noff);
|
||||
}
|
||||
|
||||
void fdt_imsic_fixup(void *fdt)
|
||||
{
|
||||
int noff = 0;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,imsics")) >= 0)
|
||||
fdt_domain_based_fixup_one(fdt, noff);
|
||||
}
|
||||
|
||||
void fdt_plic_fixup(void *fdt)
|
||||
{
|
||||
u32 *cells;
|
||||
@@ -261,10 +301,12 @@ int fdt_reserved_memory_nomap_fixup(void *fdt)
|
||||
|
||||
void fdt_fixups(void *fdt)
|
||||
{
|
||||
fdt_aplic_fixup(fdt);
|
||||
|
||||
fdt_imsic_fixup(fdt);
|
||||
|
||||
fdt_plic_fixup(fdt);
|
||||
|
||||
fdt_reserved_memory_fixup(fdt);
|
||||
fdt_pmu_fixup(fdt);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -13,12 +13,15 @@
|
||||
#include <sbi/sbi_platform.h>
|
||||
#include <sbi/sbi_scratch.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/aplic.h>
|
||||
#include <sbi_utils/irqchip/imsic.h>
|
||||
#include <sbi_utils/irqchip/plic.h>
|
||||
|
||||
#define DEFAULT_UART_FREQ 0
|
||||
#define DEFAULT_UART_BAUD 115200
|
||||
#define DEFAULT_UART_REG_SHIFT 0
|
||||
#define DEFAULT_UART_REG_IO_WIDTH 1
|
||||
#define DEFAULT_UART_REG_OFFSET 0
|
||||
|
||||
#define DEFAULT_SIFIVE_UART_FREQ 0
|
||||
#define DEFAULT_SIFIVE_UART_BAUD 115200
|
||||
@@ -213,6 +216,24 @@ int fdt_get_node_addr_size(void *fdt, int node, int index,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fdt_node_is_enabled(void *fdt, int nodeoff)
|
||||
{
|
||||
int len;
|
||||
const void *prop;
|
||||
|
||||
prop = fdt_getprop(fdt, nodeoff, "status", &len);
|
||||
if (!prop)
|
||||
return true;
|
||||
|
||||
if (!strncmp(prop, "okay", strlen("okay")))
|
||||
return true;
|
||||
|
||||
if (!strncmp(prop, "ok", strlen("ok")))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid)
|
||||
{
|
||||
int len;
|
||||
@@ -241,7 +262,7 @@ int fdt_parse_hart_id(void *fdt, int cpu_offset, u32 *hartid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid)
|
||||
int fdt_parse_max_enabled_hart_id(void *fdt, u32 *max_hartid)
|
||||
{
|
||||
u32 hartid;
|
||||
int err, cpu_offset, cpus_offset;
|
||||
@@ -262,6 +283,9 @@ int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid)
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
if (!fdt_node_is_enabled(fdt, cpu_offset))
|
||||
continue;
|
||||
|
||||
if (hartid > *max_hartid)
|
||||
*max_hartid = hartid;
|
||||
}
|
||||
@@ -447,6 +471,12 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
|
||||
else
|
||||
uart->reg_io_width = DEFAULT_UART_REG_IO_WIDTH;
|
||||
|
||||
val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-offset", &len);
|
||||
if (len > 0 && val)
|
||||
uart->reg_offset = fdt32_to_cpu(*val);
|
||||
else
|
||||
uart->reg_offset = DEFAULT_UART_REG_OFFSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -465,6 +495,284 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
|
||||
return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
|
||||
}
|
||||
|
||||
int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
|
||||
struct platform_uart_data *uart)
|
||||
{
|
||||
int rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
|
||||
if (nodeoffset < 0 || !uart || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
uart->addr = reg_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic)
|
||||
{
|
||||
bool child_found;
|
||||
const fdt32_t *val;
|
||||
const fdt32_t *del;
|
||||
struct imsic_data imsic;
|
||||
int i, j, d, dcnt, len, noff, rc;
|
||||
uint64_t reg_addr, reg_size;
|
||||
struct aplic_delegate_data *deleg;
|
||||
|
||||
if (nodeoff < 0 || !aplic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
memset(aplic, 0, sizeof(*aplic));
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
return SBI_ENODEV;
|
||||
aplic->addr = reg_addr;
|
||||
aplic->size = reg_size;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,num-sources", &len);
|
||||
if (len > 0)
|
||||
aplic->num_source = fdt32_to_cpu(*val);
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
|
||||
aplic->targets_mmode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aplic->num_idc = len / 2;
|
||||
goto aplic_msi_parent_done;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "msi-parent", &len);
|
||||
if (val && len >= sizeof(fdt32_t)) {
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
rc = fdt_parse_imsic_node(fdt, noff, &imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = imsic_data_check(&imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
aplic->targets_mmode = imsic.targets_mmode;
|
||||
|
||||
if (imsic.targets_mmode) {
|
||||
aplic->has_msicfg_mmode = true;
|
||||
aplic->msicfg_mmode.lhxs = imsic.guest_index_bits;
|
||||
aplic->msicfg_mmode.lhxw = imsic.hart_index_bits;
|
||||
aplic->msicfg_mmode.hhxw = imsic.group_index_bits;
|
||||
aplic->msicfg_mmode.hhxs = imsic.group_index_shift;
|
||||
if (aplic->msicfg_mmode.hhxs <
|
||||
(2 * IMSIC_MMIO_PAGE_SHIFT))
|
||||
return SBI_EINVAL;
|
||||
aplic->msicfg_mmode.hhxs -= 24;
|
||||
aplic->msicfg_mmode.base_addr = imsic.regs[0].addr;
|
||||
} else {
|
||||
goto aplic_msi_parent_done;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,children", &len);
|
||||
if (!val || len < sizeof(fdt32_t))
|
||||
goto aplic_msi_parent_done;
|
||||
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
val = fdt_getprop(fdt, noff, "msi-parent", &len);
|
||||
if (!val || len < sizeof(fdt32_t))
|
||||
goto aplic_msi_parent_done;
|
||||
|
||||
noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val));
|
||||
if (noff < 0)
|
||||
return noff;
|
||||
|
||||
rc = fdt_parse_imsic_node(fdt, noff, &imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = imsic_data_check(&imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!imsic.targets_mmode) {
|
||||
aplic->has_msicfg_smode = true;
|
||||
aplic->msicfg_smode.lhxs = imsic.guest_index_bits;
|
||||
aplic->msicfg_smode.lhxw = imsic.hart_index_bits;
|
||||
aplic->msicfg_smode.hhxw = imsic.group_index_bits;
|
||||
aplic->msicfg_smode.hhxs = imsic.group_index_shift;
|
||||
if (aplic->msicfg_smode.hhxs <
|
||||
(2 * IMSIC_MMIO_PAGE_SHIFT))
|
||||
return SBI_EINVAL;
|
||||
aplic->msicfg_smode.hhxs -= 24;
|
||||
aplic->msicfg_smode.base_addr = imsic.regs[0].addr;
|
||||
}
|
||||
}
|
||||
aplic_msi_parent_done:
|
||||
|
||||
for (d = 0; d < APLIC_MAX_DELEGATE; d++) {
|
||||
deleg = &aplic->delegate[d];
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
}
|
||||
|
||||
del = fdt_getprop(fdt, nodeoff, "riscv,delegate", &len);
|
||||
if (!del || len < (3 * sizeof(fdt32_t)))
|
||||
goto skip_delegate_parse;
|
||||
d = 0;
|
||||
dcnt = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < dcnt; i += 3) {
|
||||
if (d >= APLIC_MAX_DELEGATE)
|
||||
break;
|
||||
deleg = &aplic->delegate[d];
|
||||
|
||||
deleg->first_irq = fdt32_to_cpu(del[i + 1]);
|
||||
deleg->last_irq = fdt32_to_cpu(del[i + 2]);
|
||||
deleg->child_index = 0;
|
||||
|
||||
child_found = false;
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,children", &len);
|
||||
if (!val || len < sizeof(fdt32_t)) {
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
continue;
|
||||
}
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (j = 0; j < len; j++) {
|
||||
if (del[i] != val[j])
|
||||
continue;
|
||||
deleg->child_index = j;
|
||||
child_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_found) {
|
||||
d++;
|
||||
} else {
|
||||
deleg->first_irq = 0;
|
||||
deleg->last_irq = 0;
|
||||
deleg->child_index = 0;
|
||||
}
|
||||
}
|
||||
skip_delegate_parse:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool fdt_check_imsic_mlevel(void *fdt)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
int i, len, noff = 0;
|
||||
|
||||
if (!fdt)
|
||||
return false;
|
||||
|
||||
while ((noff = fdt_node_offset_by_compatible(fdt, noff,
|
||||
"riscv,imsics")) >= 0) {
|
||||
val = fdt_getprop(fdt, noff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
struct imsic_regs *regs;
|
||||
uint64_t reg_addr, reg_size;
|
||||
int i, rc, len, nr_parent_irqs;
|
||||
|
||||
if (nodeoff < 0 || !imsic || !fdt)
|
||||
return SBI_ENODEV;
|
||||
|
||||
imsic->targets_mmode = false;
|
||||
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
|
||||
if (val && len > sizeof(fdt32_t)) {
|
||||
len = len / sizeof(fdt32_t);
|
||||
nr_parent_irqs = len / 2;
|
||||
for (i = 0; i < len; i += 2) {
|
||||
if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
|
||||
imsic->targets_mmode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
return SBI_EINVAL;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,guest-index-bits", &len);
|
||||
if (val && len > 0)
|
||||
imsic->guest_index_bits = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->guest_index_bits = 0;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,hart-index-bits", &len);
|
||||
if (val && len > 0) {
|
||||
imsic->hart_index_bits = fdt32_to_cpu(*val);
|
||||
} else {
|
||||
imsic->hart_index_bits = sbi_fls(nr_parent_irqs);
|
||||
if ((1UL << imsic->hart_index_bits) < nr_parent_irqs)
|
||||
imsic->hart_index_bits++;
|
||||
}
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,group-index-bits", &len);
|
||||
if (val && len > 0)
|
||||
imsic->group_index_bits = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->group_index_bits = 0;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,group-index-shift", &len);
|
||||
if (val && len > 0)
|
||||
imsic->group_index_shift = fdt32_to_cpu(*val);
|
||||
else
|
||||
imsic->group_index_shift = 2 * IMSIC_MMIO_PAGE_SHIFT;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "riscv,num-ids", &len);
|
||||
if (val && len > 0)
|
||||
imsic->num_ids = fdt32_to_cpu(*val);
|
||||
else
|
||||
return SBI_EINVAL;
|
||||
|
||||
for (i = 0; i < IMSIC_MAX_REGS; i++) {
|
||||
regs = &imsic->regs[i];
|
||||
regs->addr = 0;
|
||||
regs->size = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < (IMSIC_MAX_REGS - 1); i++) {
|
||||
regs = &imsic->regs[i];
|
||||
|
||||
rc = fdt_get_node_addr_size(fdt, nodeoff, i,
|
||||
®_addr, ®_size);
|
||||
if (rc < 0 || !reg_addr || !reg_size)
|
||||
break;
|
||||
regs->addr = reg_addr;
|
||||
regs->size = reg_size;
|
||||
};
|
||||
if (!imsic->regs[0].size)
|
||||
return SBI_EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
|
||||
{
|
||||
int len, rc;
|
||||
|
@@ -53,7 +53,7 @@ int fdt_pmu_fixup(void *fdt)
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmcounters");
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,event-to-mhpmevent");
|
||||
fdt_delprop(fdt, pmu_offset, "riscv,raw-event-to-mhpmcounters");
|
||||
if (!sbi_hart_has_feature(scratch, SBI_HART_HAS_SSCOFPMF))
|
||||
if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
|
||||
fdt_delprop(fdt, pmu_offset, "interrupts-extended");
|
||||
|
||||
return 0;
|
||||
|
@@ -12,11 +12,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/gpio/fdt_gpio.h>
|
||||
|
||||
extern struct fdt_gpio fdt_gpio_sifive;
|
||||
|
||||
static struct fdt_gpio *gpio_drivers[] = {
|
||||
&fdt_gpio_sifive
|
||||
};
|
||||
/* List of FDT gpio drivers generated at compile time */
|
||||
extern struct fdt_gpio *fdt_gpio_drivers[];
|
||||
extern unsigned long fdt_gpio_drivers_size;
|
||||
|
||||
static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
|
||||
{
|
||||
@@ -25,9 +23,9 @@ static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
|
||||
if (!chip)
|
||||
return NULL;
|
||||
|
||||
for (pos = 0; pos < array_size(gpio_drivers); pos++) {
|
||||
if (chip->driver == gpio_drivers[pos])
|
||||
return gpio_drivers[pos];
|
||||
for (pos = 0; pos < fdt_gpio_drivers_size; pos++) {
|
||||
if (chip->driver == fdt_gpio_drivers[pos])
|
||||
return fdt_gpio_drivers[pos];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -49,8 +47,8 @@ static int fdt_gpio_init(void *fdt, u32 phandle)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Try all GPIO drivers one-by-one */
|
||||
for (pos = 0; pos < array_size(gpio_drivers); pos++) {
|
||||
drv = gpio_drivers[pos];
|
||||
for (pos = 0; pos < fdt_gpio_drivers_size; pos++) {
|
||||
drv = fdt_gpio_drivers[pos];
|
||||
|
||||
match = fdt_match_node(fdt, nodeoff, drv->match_table);
|
||||
if (match && drv->init) {
|
||||
|
3
lib/utils/gpio/fdt_gpio_drivers.carray
Normal file
3
lib/utils/gpio/fdt_gpio_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/gpio/fdt_gpio.h
|
||||
TYPE: struct fdt_gpio
|
||||
NAME: fdt_gpio_drivers
|
@@ -8,5 +8,9 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += gpio/fdt_gpio.o
|
||||
libsbiutils-objs-y += gpio/fdt_gpio_drivers.o
|
||||
|
||||
carray-fdt_gpio_drivers-y += fdt_gpio_sifive
|
||||
libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
|
||||
|
||||
libsbiutils-objs-y += gpio/gpio.o
|
||||
|
@@ -16,13 +16,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/i2c/fdt_i2c.h>
|
||||
|
||||
#include <sbi/sbi_console.h>
|
||||
|
||||
extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
|
||||
|
||||
static struct fdt_i2c_adapter *i2c_adapter_drivers[] = {
|
||||
&fdt_i2c_adapter_sifive
|
||||
};
|
||||
/* List of FDT i2c adapter drivers generated at compile time */
|
||||
extern struct fdt_i2c_adapter *fdt_i2c_adapter_drivers[];
|
||||
extern unsigned long fdt_i2c_adapter_drivers_size;
|
||||
|
||||
static int fdt_i2c_adapter_init(void *fdt, int nodeoff)
|
||||
{
|
||||
@@ -31,8 +27,8 @@ static int fdt_i2c_adapter_init(void *fdt, int nodeoff)
|
||||
const struct fdt_match *match;
|
||||
|
||||
/* Try all I2C drivers one-by-one */
|
||||
for (pos = 0; pos < array_size(i2c_adapter_drivers); pos++) {
|
||||
drv = i2c_adapter_drivers[pos];
|
||||
for (pos = 0; pos < fdt_i2c_adapter_drivers_size; pos++) {
|
||||
drv = fdt_i2c_adapter_drivers[pos];
|
||||
match = fdt_match_node(fdt, nodeoff, drv->match_table);
|
||||
if (match && drv->init) {
|
||||
rc = drv->init(fdt, nodeoff, match);
|
||||
|
3
lib/utils/i2c/fdt_i2c_adapter_drivers.carray
Normal file
3
lib/utils/i2c/fdt_i2c_adapter_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/i2c/fdt_i2c.h
|
||||
TYPE: struct fdt_i2c_adapter
|
||||
NAME: fdt_i2c_adapter_drivers
|
@@ -56,13 +56,13 @@ extern struct fdt_i2c_adapter fdt_i2c_adapter_sifive;
|
||||
static inline void sifive_i2c_setreg(struct sifive_i2c_adapter *adap,
|
||||
uint8_t reg, uint8_t value)
|
||||
{
|
||||
writel(value, (volatile void *)adap->addr + reg);
|
||||
writel(value, (volatile char *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static inline uint8_t sifive_i2c_getreg(struct sifive_i2c_adapter *adap,
|
||||
uint8_t reg)
|
||||
{
|
||||
return readl((volatile void *)adap->addr + reg);
|
||||
return readl((volatile char *)adap->addr + reg);
|
||||
}
|
||||
|
||||
static int sifive_i2c_adapter_rxack(struct sifive_i2c_adapter *adap)
|
||||
|
@@ -8,5 +8,9 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += i2c/i2c.o
|
||||
|
||||
libsbiutils-objs-y += i2c/fdt_i2c.o
|
||||
libsbiutils-objs-y += i2c/fdt_i2c_adapter_drivers.o
|
||||
|
||||
carray-fdt_i2c_adapter_drivers-y += fdt_i2c_adapter_sifive
|
||||
libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
|
||||
|
@@ -74,7 +74,7 @@ int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
|
||||
|
||||
/* Sanity checks */
|
||||
if (!mswi || (mswi->addr & (ACLINT_MSWI_ALIGN - 1)) ||
|
||||
(mswi->size < ACLINT_MSWI_SIZE) ||
|
||||
(mswi->size < (mswi->hart_count * sizeof(u32))) ||
|
||||
(mswi->first_hartid >= SBI_HARTMASK_MAX_BITS) ||
|
||||
(mswi->hart_count > ACLINT_MSWI_MAX_HARTS))
|
||||
return SBI_EINVAL;
|
||||
|
@@ -12,11 +12,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/ipi/fdt_ipi.h>
|
||||
|
||||
extern struct fdt_ipi fdt_ipi_mswi;
|
||||
|
||||
static struct fdt_ipi *ipi_drivers[] = {
|
||||
&fdt_ipi_mswi
|
||||
};
|
||||
/* List of FDT ipi drivers generated at compile time */
|
||||
extern struct fdt_ipi *fdt_ipi_drivers[];
|
||||
extern unsigned long fdt_ipi_drivers_size;
|
||||
|
||||
static struct fdt_ipi dummy = {
|
||||
.match_table = NULL,
|
||||
@@ -47,8 +45,8 @@ static int fdt_ipi_cold_init(void)
|
||||
const struct fdt_match *match;
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(ipi_drivers); pos++) {
|
||||
drv = ipi_drivers[pos];
|
||||
for (pos = 0; pos < fdt_ipi_drivers_size; pos++) {
|
||||
drv = fdt_ipi_drivers[pos];
|
||||
|
||||
noff = -1;
|
||||
while ((noff = fdt_find_match(fdt, noff,
|
||||
|
3
lib/utils/ipi/fdt_ipi_drivers.carray
Normal file
3
lib/utils/ipi/fdt_ipi_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/ipi/fdt_ipi.h
|
||||
TYPE: struct fdt_ipi
|
||||
NAME: fdt_ipi_drivers
|
@@ -51,7 +51,7 @@ static int ipi_mswi_cold_init(void *fdt, int nodeoff,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clint_offset = CLINT_MSWI_OFFSET;
|
||||
static const unsigned long clint_offset = CLINT_MSWI_OFFSET;
|
||||
|
||||
static const struct fdt_match ipi_mswi_match[] = {
|
||||
{ .compatible = "riscv,clint0", .data = &clint_offset },
|
||||
|
@@ -8,5 +8,9 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += ipi/aclint_mswi.o
|
||||
|
||||
libsbiutils-objs-y += ipi/fdt_ipi.o
|
||||
libsbiutils-objs-y += ipi/fdt_ipi_drivers.o
|
||||
|
||||
carray-fdt_ipi_drivers-y += fdt_ipi_mswi
|
||||
libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
|
||||
|
279
lib/utils/irqchip/aplic.c
Normal file
279
lib/utils/irqchip/aplic.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/irqchip/aplic.h>
|
||||
|
||||
#define APLIC_MAX_IDC (1UL << 14)
|
||||
#define APLIC_MAX_SOURCE 1024
|
||||
|
||||
#define APLIC_DOMAINCFG 0x0000
|
||||
#define APLIC_DOMAINCFG_IE (1 << 8)
|
||||
#define APLIC_DOMAINCFG_DM (1 << 2)
|
||||
#define APLIC_DOMAINCFG_BE (1 << 0)
|
||||
|
||||
#define APLIC_SOURCECFG_BASE 0x0004
|
||||
#define APLIC_SOURCECFG_D (1 << 10)
|
||||
#define APLIC_SOURCECFG_CHILDIDX_MASK 0x000003ff
|
||||
#define APLIC_SOURCECFG_SM_MASK 0x00000007
|
||||
#define APLIC_SOURCECFG_SM_INACTIVE 0x0
|
||||
#define APLIC_SOURCECFG_SM_DETACH 0x1
|
||||
#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4
|
||||
#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5
|
||||
#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6
|
||||
#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7
|
||||
|
||||
#define APLIC_MMSICFGADDR 0x1bc0
|
||||
#define APLIC_MMSICFGADDRH 0x1bc4
|
||||
#define APLIC_SMSICFGADDR 0x1bc8
|
||||
#define APLIC_SMSICFGADDRH 0x1bcc
|
||||
|
||||
#define APLIC_xMSICFGADDRH_L (1UL << 31)
|
||||
#define APLIC_xMSICFGADDRH_HHXS_MASK 0x1f
|
||||
#define APLIC_xMSICFGADDRH_HHXS_SHIFT 24
|
||||
#define APLIC_xMSICFGADDRH_LHXS_MASK 0x7
|
||||
#define APLIC_xMSICFGADDRH_LHXS_SHIFT 20
|
||||
#define APLIC_xMSICFGADDRH_HHXW_MASK 0x7
|
||||
#define APLIC_xMSICFGADDRH_HHXW_SHIFT 16
|
||||
#define APLIC_xMSICFGADDRH_LHXW_MASK 0xf
|
||||
#define APLIC_xMSICFGADDRH_LHXW_SHIFT 12
|
||||
#define APLIC_xMSICFGADDRH_BAPPN_MASK 0xfff
|
||||
|
||||
#define APLIC_xMSICFGADDR_PPN_SHIFT 12
|
||||
|
||||
#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
|
||||
((1UL << (__lhxs)) - 1)
|
||||
|
||||
#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
|
||||
((1UL << (__lhxw)) - 1)
|
||||
#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
|
||||
((__lhxs))
|
||||
#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
|
||||
(APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
|
||||
APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
|
||||
|
||||
#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
|
||||
((1UL << (__hhxw)) - 1)
|
||||
#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
|
||||
((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
|
||||
#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
|
||||
(APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
|
||||
APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
|
||||
|
||||
#define APLIC_SETIP_BASE 0x1c00
|
||||
#define APLIC_SETIPNUM 0x1cdc
|
||||
|
||||
#define APLIC_CLRIP_BASE 0x1d00
|
||||
#define APLIC_CLRIPNUM 0x1ddc
|
||||
|
||||
#define APLIC_SETIE_BASE 0x1e00
|
||||
#define APLIC_SETIENUM 0x1edc
|
||||
|
||||
#define APLIC_CLRIE_BASE 0x1f00
|
||||
#define APLIC_CLRIENUM 0x1fdc
|
||||
|
||||
#define APLIC_SETIPNUM_LE 0x2000
|
||||
#define APLIC_SETIPNUM_BE 0x2004
|
||||
|
||||
#define APLIC_TARGET_BASE 0x3004
|
||||
#define APLIC_TARGET_HART_IDX_SHIFT 18
|
||||
#define APLIC_TARGET_HART_IDX_MASK 0x3fff
|
||||
#define APLIC_TARGET_GUEST_IDX_SHIFT 12
|
||||
#define APLIC_TARGET_GUEST_IDX_MASK 0x3f
|
||||
#define APLIC_TARGET_IPRIO_MASK 0xff
|
||||
#define APLIC_TARGET_EIID_MASK 0x7ff
|
||||
|
||||
#define APLIC_IDC_BASE 0x4000
|
||||
#define APLIC_IDC_SIZE 32
|
||||
|
||||
#define APLIC_IDC_IDELIVERY 0x00
|
||||
|
||||
#define APLIC_IDC_IFORCE 0x04
|
||||
|
||||
#define APLIC_IDC_ITHRESHOLD 0x08
|
||||
|
||||
#define APLIC_IDC_TOPI 0x18
|
||||
#define APLIC_IDC_TOPI_ID_SHIFT 16
|
||||
#define APLIC_IDC_TOPI_ID_MASK 0x3ff
|
||||
#define APLIC_IDC_TOPI_PRIO_MASK 0xff
|
||||
|
||||
#define APLIC_IDC_CLAIMI 0x1c
|
||||
|
||||
#define APLIC_DEFAULT_PRIORITY 1
|
||||
#define APLIC_DISABLE_IDELIVERY 0
|
||||
#define APLIC_ENABLE_IDELIVERY 1
|
||||
#define APLIC_DISABLE_ITHRESHOLD 1
|
||||
#define APLIC_ENABLE_ITHRESHOLD 0
|
||||
|
||||
static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
|
||||
void *msicfgaddr, void *msicfgaddrH)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long base_ppn;
|
||||
|
||||
/* Check if MSI config is already locked */
|
||||
if (readl(msicfgaddrH) & APLIC_xMSICFGADDRH_L)
|
||||
return;
|
||||
|
||||
/* Compute the MSI base PPN */
|
||||
base_ppn = msicfg->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
|
||||
base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs);
|
||||
base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs);
|
||||
base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs);
|
||||
|
||||
/* Write the lower MSI config register */
|
||||
writel((u32)base_ppn, msicfgaddr);
|
||||
|
||||
/* Write the upper MSI config register */
|
||||
val = (((u64)base_ppn) >> 32) &
|
||||
APLIC_xMSICFGADDRH_BAPPN_MASK;
|
||||
val |= (msicfg->lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
|
||||
<< APLIC_xMSICFGADDRH_LHXW_SHIFT;
|
||||
val |= (msicfg->hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
|
||||
<< APLIC_xMSICFGADDRH_HHXW_SHIFT;
|
||||
val |= (msicfg->lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
|
||||
<< APLIC_xMSICFGADDRH_LHXS_SHIFT;
|
||||
val |= (msicfg->hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
|
||||
<< APLIC_xMSICFGADDRH_HHXS_SHIFT;
|
||||
writel(val, msicfgaddrH);
|
||||
}
|
||||
|
||||
static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
|
||||
{
|
||||
if (APLIC_xMSICFGADDRH_LHXS_MASK < msicfg->lhxs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (APLIC_xMSICFGADDRH_LHXW_MASK < msicfg->lhxw)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (APLIC_xMSICFGADDRH_HHXS_MASK < msicfg->hhxs)
|
||||
return SBI_EINVAL;
|
||||
|
||||
if (APLIC_xMSICFGADDRH_HHXW_MASK < msicfg->hhxw)
|
||||
return SBI_EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aplic_cold_irqchip_init(struct aplic_data *aplic)
|
||||
{
|
||||
int rc;
|
||||
u32 i, j, tmp;
|
||||
struct sbi_domain_memregion reg;
|
||||
struct aplic_delegate_data *deleg;
|
||||
u32 first_deleg_irq, last_deleg_irq;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!aplic ||
|
||||
!aplic->num_source || APLIC_MAX_SOURCE <= aplic->num_source ||
|
||||
APLIC_MAX_IDC <= aplic->num_idc)
|
||||
return SBI_EINVAL;
|
||||
if (aplic->targets_mmode && aplic->has_msicfg_mmode) {
|
||||
rc = aplic_check_msicfg(&aplic->msicfg_mmode);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
if (aplic->targets_mmode && aplic->has_msicfg_smode) {
|
||||
rc = aplic_check_msicfg(&aplic->msicfg_smode);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set domain configuration to 0 */
|
||||
writel(0, (void *)(aplic->addr + APLIC_DOMAINCFG));
|
||||
|
||||
/* Disable all interrupts */
|
||||
for (i = 0; i <= aplic->num_source; i++)
|
||||
writel(-1U, (void *)(aplic->addr + APLIC_CLRIE_BASE +
|
||||
(i / 32) * sizeof(u32)));
|
||||
|
||||
/* Set interrupt type and priority for all interrupts */
|
||||
for (i = 1; i <= aplic->num_source; i++) {
|
||||
/* Set IRQ source configuration to 0 */
|
||||
writel(0, (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
||||
(i - 1) * sizeof(u32)));
|
||||
/* Set IRQ target hart index and priority to 1 */
|
||||
writel(APLIC_DEFAULT_PRIORITY, (void *)(aplic->addr +
|
||||
APLIC_TARGET_BASE +
|
||||
(i - 1) * sizeof(u32)));
|
||||
}
|
||||
|
||||
/* Configure IRQ delegation */
|
||||
first_deleg_irq = -1U;
|
||||
last_deleg_irq = 0;
|
||||
for (i = 0; i < APLIC_MAX_DELEGATE; i++) {
|
||||
deleg = &aplic->delegate[i];
|
||||
if (!deleg->first_irq || !deleg->last_irq)
|
||||
continue;
|
||||
if (aplic->num_source < deleg->first_irq ||
|
||||
aplic->num_source < deleg->last_irq)
|
||||
continue;
|
||||
if (APLIC_SOURCECFG_CHILDIDX_MASK < deleg->child_index)
|
||||
continue;
|
||||
if (deleg->first_irq > deleg->last_irq) {
|
||||
tmp = deleg->first_irq;
|
||||
deleg->first_irq = deleg->last_irq;
|
||||
deleg->last_irq = tmp;
|
||||
}
|
||||
if (deleg->first_irq < first_deleg_irq)
|
||||
first_deleg_irq = deleg->first_irq;
|
||||
if (last_deleg_irq < deleg->last_irq)
|
||||
last_deleg_irq = deleg->last_irq;
|
||||
for (j = deleg->first_irq; j <= deleg->last_irq; j++)
|
||||
writel(APLIC_SOURCECFG_D | deleg->child_index,
|
||||
(void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
||||
(j - 1) * sizeof(u32)));
|
||||
}
|
||||
|
||||
/* Default initialization of IDC structures */
|
||||
for (i = 0; i < aplic->num_idc; i++) {
|
||||
writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
|
||||
i * APLIC_IDC_SIZE + APLIC_IDC_IDELIVERY));
|
||||
writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
|
||||
i * APLIC_IDC_SIZE + APLIC_IDC_IFORCE));
|
||||
writel(APLIC_DISABLE_ITHRESHOLD, (void *)(aplic->addr +
|
||||
APLIC_IDC_BASE +
|
||||
(i * APLIC_IDC_SIZE) +
|
||||
APLIC_IDC_ITHRESHOLD));
|
||||
}
|
||||
|
||||
/* MSI configuration */
|
||||
if (aplic->targets_mmode && aplic->has_msicfg_mmode) {
|
||||
aplic_writel_msicfg(&aplic->msicfg_mmode,
|
||||
(void *)(aplic->addr + APLIC_MMSICFGADDR),
|
||||
(void *)(aplic->addr + APLIC_MMSICFGADDRH));
|
||||
}
|
||||
if (aplic->targets_mmode && aplic->has_msicfg_smode) {
|
||||
aplic_writel_msicfg(&aplic->msicfg_smode,
|
||||
(void *)(aplic->addr + APLIC_SMSICFGADDR),
|
||||
(void *)(aplic->addr + APLIC_SMSICFGADDRH));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add APLIC region to the root domain if:
|
||||
* 1) It targets M-mode of any HART directly or via MSIs
|
||||
* 2) All interrupts are delegated to some child APLIC
|
||||
*/
|
||||
if (aplic->targets_mmode ||
|
||||
((first_deleg_irq < last_deleg_irq) &&
|
||||
(last_deleg_irq == aplic->num_source) &&
|
||||
(first_deleg_irq == 1))) {
|
||||
sbi_domain_memregion_init(aplic->addr, aplic->size,
|
||||
SBI_DOMAIN_MEMREGION_MMIO, ®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@@ -12,38 +12,54 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip.h>
|
||||
|
||||
extern struct fdt_irqchip fdt_irqchip_plic;
|
||||
/* List of FDT irqchip drivers generated at compile time */
|
||||
extern struct fdt_irqchip *fdt_irqchip_drivers[];
|
||||
extern unsigned long fdt_irqchip_drivers_size;
|
||||
|
||||
static struct fdt_irqchip *irqchip_drivers[] = {
|
||||
&fdt_irqchip_plic
|
||||
};
|
||||
#define FDT_IRQCHIP_MAX_DRIVERS 8
|
||||
|
||||
static struct fdt_irqchip *current_driver = NULL;
|
||||
static struct fdt_irqchip *current_drivers[FDT_IRQCHIP_MAX_DRIVERS] = {0};
|
||||
static int current_drivers_count;
|
||||
|
||||
void fdt_irqchip_exit(void)
|
||||
{
|
||||
if (current_driver && current_driver->exit)
|
||||
current_driver->exit();
|
||||
int i;
|
||||
|
||||
for (i = 0; i < current_drivers_count; i++) {
|
||||
if (!current_drivers[i] || !current_drivers[i]->exit)
|
||||
continue;
|
||||
current_drivers[i]->exit();
|
||||
}
|
||||
}
|
||||
|
||||
static int fdt_irqchip_warm_init(void)
|
||||
{
|
||||
if (current_driver && current_driver->warm_init)
|
||||
return current_driver->warm_init();
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < current_drivers_count; i++) {
|
||||
if (!current_drivers[i] || !current_drivers[i]->warm_init)
|
||||
continue;
|
||||
rc = current_drivers[i]->warm_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fdt_irqchip_cold_init(void)
|
||||
{
|
||||
bool drv_added;
|
||||
int pos, noff, rc;
|
||||
struct fdt_irqchip *drv;
|
||||
const struct fdt_match *match;
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(irqchip_drivers); pos++) {
|
||||
drv = irqchip_drivers[pos];
|
||||
for (pos = 0; pos < fdt_irqchip_drivers_size; pos++) {
|
||||
drv = fdt_irqchip_drivers[pos];
|
||||
|
||||
noff = -1;
|
||||
drv_added = false;
|
||||
while ((noff = fdt_find_match(fdt, noff,
|
||||
drv->match_table, &match)) >= 0) {
|
||||
if (drv->cold_init) {
|
||||
@@ -53,10 +69,15 @@ static int fdt_irqchip_cold_init(void)
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
current_driver = drv;
|
||||
|
||||
if (drv_added)
|
||||
continue;
|
||||
|
||||
current_drivers[current_drivers_count++] = drv;
|
||||
drv_added = true;
|
||||
}
|
||||
|
||||
if (current_driver)
|
||||
if (FDT_IRQCHIP_MAX_DRIVERS <= current_drivers_count)
|
||||
break;
|
||||
}
|
||||
|
||||
|
56
lib/utils/irqchip/fdt_irqchip_aplic.c
Normal file
56
lib/utils/irqchip/fdt_irqchip_aplic.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip.h>
|
||||
#include <sbi_utils/irqchip/aplic.h>
|
||||
|
||||
#define APLIC_MAX_NR 16
|
||||
|
||||
static unsigned long aplic_count = 0;
|
||||
static struct aplic_data aplic[APLIC_MAX_NR];
|
||||
|
||||
static int irqchip_aplic_warm_init(void)
|
||||
{
|
||||
/* Nothing to do here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irqchip_aplic_cold_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct aplic_data *pd;
|
||||
|
||||
if (APLIC_MAX_NR <= aplic_count)
|
||||
return SBI_ENOSPC;
|
||||
pd = &aplic[aplic_count++];
|
||||
|
||||
rc = fdt_parse_aplic_node(fdt, nodeoff, pd);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return aplic_cold_irqchip_init(pd);
|
||||
}
|
||||
|
||||
static const struct fdt_match irqchip_aplic_match[] = {
|
||||
{ .compatible = "riscv,aplic" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_irqchip fdt_irqchip_aplic = {
|
||||
.match_table = irqchip_aplic_match,
|
||||
.cold_init = irqchip_aplic_cold_init,
|
||||
.warm_init = irqchip_aplic_warm_init,
|
||||
.exit = NULL,
|
||||
};
|
3
lib/utils/irqchip/fdt_irqchip_drivers.carray
Normal file
3
lib/utils/irqchip/fdt_irqchip_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/irqchip/fdt_irqchip.h
|
||||
TYPE: struct fdt_irqchip
|
||||
NAME: fdt_irqchip_drivers
|
106
lib/utils/irqchip/fdt_irqchip_imsic.c
Normal file
106
lib/utils/irqchip/fdt_irqchip_imsic.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip.h>
|
||||
#include <sbi_utils/irqchip/imsic.h>
|
||||
|
||||
#define IMSIC_MAX_NR 16
|
||||
|
||||
static unsigned long imsic_count = 0;
|
||||
static struct imsic_data imsic[IMSIC_MAX_NR];
|
||||
|
||||
static int irqchip_imsic_update_hartid_table(void *fdt, int nodeoff,
|
||||
struct imsic_data *id)
|
||||
{
|
||||
const fdt32_t *val;
|
||||
u32 phandle, hwirq, hartid;
|
||||
int i, err, count, cpu_offset, cpu_intc_offset;
|
||||
|
||||
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count);
|
||||
if (!val || count < sizeof(fdt32_t))
|
||||
return SBI_EINVAL;
|
||||
count = count / sizeof(fdt32_t);
|
||||
|
||||
for (i = 0; i < count; i += 2) {
|
||||
phandle = fdt32_to_cpu(val[i]);
|
||||
hwirq = fdt32_to_cpu(val[i + 1]);
|
||||
|
||||
cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
|
||||
if (cpu_intc_offset < 0)
|
||||
continue;
|
||||
|
||||
cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
|
||||
if (cpu_offset < 0)
|
||||
continue;
|
||||
|
||||
err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
|
||||
if (err)
|
||||
return SBI_EINVAL;
|
||||
if (SBI_HARTMASK_MAX_BITS <= hartid)
|
||||
return SBI_EINVAL;
|
||||
|
||||
switch (hwirq) {
|
||||
case IRQ_M_EXT:
|
||||
err = imsic_map_hartid_to_data(hartid, id, i / 2);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irqchip_imsic_cold_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct imsic_data *id;
|
||||
|
||||
if (IMSIC_MAX_NR <= imsic_count)
|
||||
return SBI_ENOSPC;
|
||||
id = &imsic[imsic_count];
|
||||
|
||||
rc = fdt_parse_imsic_node(fdt, nodeoff, id);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!id->targets_mmode)
|
||||
return 0;
|
||||
|
||||
rc = irqchip_imsic_update_hartid_table(fdt, nodeoff, id);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = imsic_cold_irqchip_init(id);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
imsic_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match irqchip_imsic_match[] = {
|
||||
{ .compatible = "riscv,imsics" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_irqchip fdt_irqchip_imsic = {
|
||||
.match_table = irqchip_imsic_match,
|
||||
.cold_init = irqchip_imsic_cold_init,
|
||||
.warm_init = imsic_warm_irqchip_init,
|
||||
};
|
@@ -24,6 +24,38 @@ static struct plic_data plic[PLIC_MAX_NR];
|
||||
static struct plic_data *plic_hartid2data[SBI_HARTMASK_MAX_BITS];
|
||||
static int plic_hartid2context[SBI_HARTMASK_MAX_BITS][2];
|
||||
|
||||
void fdt_plic_priority_save(u8 *priority)
|
||||
{
|
||||
struct plic_data *plic = plic_hartid2data[current_hartid()];
|
||||
|
||||
plic_priority_save(plic, priority);
|
||||
}
|
||||
|
||||
void fdt_plic_priority_restore(const u8 *priority)
|
||||
{
|
||||
struct plic_data *plic = plic_hartid2data[current_hartid()];
|
||||
|
||||
plic_priority_restore(plic, priority);
|
||||
}
|
||||
|
||||
void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
plic_context_save(plic_hartid2data[hartid],
|
||||
plic_hartid2context[hartid][smode],
|
||||
enable, threshold);
|
||||
}
|
||||
|
||||
void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
|
||||
plic_context_restore(plic_hartid2data[hartid],
|
||||
plic_hartid2context[hartid][smode],
|
||||
enable, threshold);
|
||||
}
|
||||
|
||||
static int irqchip_plic_warm_init(void)
|
||||
{
|
||||
u32 hartid = current_hartid();
|
||||
@@ -116,7 +148,14 @@ static int irqchip_plic_cold_init(void *fdt, int nodeoff,
|
||||
|
||||
static void thead_plic_plat_init(struct plic_data *pd)
|
||||
{
|
||||
writel_relaxed(BIT(0), (void *)pd->addr + THEAD_PLIC_CTRL_REG);
|
||||
writel_relaxed(BIT(0), (char *)pd->addr + THEAD_PLIC_CTRL_REG);
|
||||
}
|
||||
|
||||
void thead_plic_restore(void)
|
||||
{
|
||||
struct plic_data *plic = plic_hartid2data[current_hartid()];
|
||||
|
||||
thead_plic_plat_init(plic);
|
||||
}
|
||||
|
||||
static const struct fdt_match irqchip_plic_match[] = {
|
||||
|
326
lib/utils/irqchip/imsic.c
Normal file
326
lib/utils/irqchip/imsic.c
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
||||
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_asm.h>
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/riscv_encoding.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_domain.h>
|
||||
#include <sbi/sbi_hartmask.h>
|
||||
#include <sbi/sbi_ipi.h>
|
||||
#include <sbi/sbi_irqchip.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi_utils/irqchip/imsic.h>
|
||||
|
||||
#define IMSIC_MMIO_PAGE_LE 0x00
|
||||
#define IMSIC_MMIO_PAGE_BE 0x04
|
||||
|
||||
#define IMSIC_MIN_ID 63
|
||||
#define IMSIC_MAX_ID 2047
|
||||
|
||||
#define IMSIC_EIDELIVERY 0x70
|
||||
|
||||
#define IMSIC_EITHRESHOLD 0x72
|
||||
|
||||
#define IMSIC_TOPEI 0x76
|
||||
#define IMSIC_TOPEI_ID_SHIFT 16
|
||||
#define IMSIC_TOPEI_ID_MASK 0x7ff
|
||||
#define IMSIC_TOPEI_PRIO_MASK 0x7ff
|
||||
|
||||
#define IMSIC_EIP0 0x80
|
||||
|
||||
#define IMSIC_EIP63 0xbf
|
||||
|
||||
#define IMSIC_EIPx_BITS 32
|
||||
|
||||
#define IMSIC_EIE0 0xc0
|
||||
|
||||
#define IMSIC_EIE63 0xff
|
||||
|
||||
#define IMSIC_EIEx_BITS 32
|
||||
|
||||
#define IMSIC_DISABLE_EIDELIVERY 0
|
||||
#define IMSIC_ENABLE_EIDELIVERY 1
|
||||
#define IMSIC_DISABLE_EITHRESHOLD 1
|
||||
#define IMSIC_ENABLE_EITHRESHOLD 0
|
||||
|
||||
#define IMSIC_IPI_ID 1
|
||||
|
||||
#define imsic_csr_write(__c, __v) \
|
||||
do { \
|
||||
csr_write(CSR_MISELECT, __c); \
|
||||
csr_write(CSR_MIREG, __v); \
|
||||
} while (0)
|
||||
|
||||
#define imsic_csr_read(__c) \
|
||||
({ \
|
||||
unsigned long __v; \
|
||||
csr_write(CSR_MISELECT, __c); \
|
||||
__v = csr_read(CSR_MIREG); \
|
||||
__v; \
|
||||
})
|
||||
|
||||
#define imsic_csr_set(__c, __v) \
|
||||
do { \
|
||||
csr_write(CSR_MISELECT, __c); \
|
||||
csr_set(CSR_MIREG, __v); \
|
||||
} while (0)
|
||||
|
||||
#define imsic_csr_clear(__c, __v) \
|
||||
do { \
|
||||
csr_write(CSR_MISELECT, __c); \
|
||||
csr_clear(CSR_MIREG, __v); \
|
||||
} while (0)
|
||||
|
||||
static struct imsic_data *imsic_hartid2data[SBI_HARTMASK_MAX_BITS];
|
||||
static int imsic_hartid2file[SBI_HARTMASK_MAX_BITS];
|
||||
|
||||
int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file)
|
||||
{
|
||||
if (!imsic || !imsic->targets_mmode ||
|
||||
(SBI_HARTMASK_MAX_BITS <= hartid))
|
||||
return SBI_EINVAL;
|
||||
|
||||
imsic_hartid2data[hartid] = imsic;
|
||||
imsic_hartid2file[hartid] = file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct imsic_data *imsic_get_data(u32 hartid)
|
||||
{
|
||||
if (SBI_HARTMASK_MAX_BITS <= hartid)
|
||||
return NULL;
|
||||
return imsic_hartid2data[hartid];
|
||||
}
|
||||
|
||||
int imsic_get_target_file(u32 hartid)
|
||||
{
|
||||
if ((SBI_HARTMASK_MAX_BITS <= hartid) ||
|
||||
!imsic_hartid2data[hartid])
|
||||
return SBI_ENOENT;
|
||||
return imsic_hartid2file[hartid];
|
||||
}
|
||||
|
||||
static int imsic_external_irqfn(struct sbi_trap_regs *regs)
|
||||
{
|
||||
ulong mirq;
|
||||
|
||||
while ((mirq = csr_swap(CSR_MTOPEI, 0))) {
|
||||
mirq = (mirq >> IMSIC_TOPEI_ID_SHIFT);
|
||||
|
||||
switch (mirq) {
|
||||
case IMSIC_IPI_ID:
|
||||
sbi_ipi_process();
|
||||
break;
|
||||
default:
|
||||
sbi_printf("%s: unhandled IRQ%d\n",
|
||||
__func__, (u32)mirq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imsic_ipi_send(u32 target_hart)
|
||||
{
|
||||
unsigned long reloff;
|
||||
struct imsic_regs *regs;
|
||||
struct imsic_data *data = imsic_hartid2data[target_hart];
|
||||
int file = imsic_hartid2file[target_hart];
|
||||
|
||||
if (!data || !data->targets_mmode)
|
||||
return;
|
||||
|
||||
regs = &data->regs[0];
|
||||
reloff = file * (1UL << data->guest_index_bits) * IMSIC_MMIO_PAGE_SZ;
|
||||
while (regs->size && (regs->size <= reloff)) {
|
||||
reloff -= regs->size;
|
||||
regs++;
|
||||
}
|
||||
|
||||
if (regs->size && (reloff < regs->size))
|
||||
writel(IMSIC_IPI_ID,
|
||||
(void *)(regs->addr + reloff + IMSIC_MMIO_PAGE_LE));
|
||||
}
|
||||
|
||||
static struct sbi_ipi_device imsic_ipi_device = {
|
||||
.name = "aia-imsic",
|
||||
.ipi_send = imsic_ipi_send
|
||||
};
|
||||
|
||||
static void imsic_local_eix_update(unsigned long base_id,
|
||||
unsigned long num_id, bool pend, bool val)
|
||||
{
|
||||
unsigned long i, isel, ireg;
|
||||
unsigned long id = base_id, last_id = base_id + num_id;
|
||||
|
||||
while (id < last_id) {
|
||||
isel = id / __riscv_xlen;
|
||||
isel *= __riscv_xlen / IMSIC_EIPx_BITS;
|
||||
isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
|
||||
|
||||
ireg = 0;
|
||||
for (i = id & (__riscv_xlen - 1);
|
||||
(id < last_id) && (i < __riscv_xlen); i++) {
|
||||
ireg |= BIT(i);
|
||||
id++;
|
||||
}
|
||||
|
||||
if (val)
|
||||
imsic_csr_set(isel, ireg);
|
||||
else
|
||||
imsic_csr_clear(isel, ireg);
|
||||
}
|
||||
}
|
||||
|
||||
void imsic_local_irqchip_init(void)
|
||||
{
|
||||
/*
|
||||
* This function is expected to be called from:
|
||||
* 1) nascent_init() platform callback which is called
|
||||
* very early on each HART in boot-up path and and
|
||||
* HSM resume path.
|
||||
* 2) irqchip_init() platform callback which is called
|
||||
* in boot-up path.
|
||||
*/
|
||||
|
||||
/* Setup threshold to allow all enabled interrupts */
|
||||
imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
|
||||
|
||||
/* Enable interrupt delivery */
|
||||
imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
|
||||
|
||||
/* Enable IPI */
|
||||
imsic_local_eix_update(IMSIC_IPI_ID, 1, false, true);
|
||||
}
|
||||
|
||||
int imsic_warm_irqchip_init(void)
|
||||
{
|
||||
struct imsic_data *imsic = imsic_hartid2data[current_hartid()];
|
||||
|
||||
/* Sanity checks */
|
||||
if (!imsic || !imsic->targets_mmode)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Disable all interrupts */
|
||||
imsic_local_eix_update(1, imsic->num_ids, false, false);
|
||||
|
||||
/* Clear IPI pending */
|
||||
imsic_local_eix_update(IMSIC_IPI_ID, 1, true, false);
|
||||
|
||||
/* Local IMSIC initialization */
|
||||
imsic_local_irqchip_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imsic_data_check(struct imsic_data *imsic)
|
||||
{
|
||||
u32 i, tmp;
|
||||
unsigned long base_addr, addr, mask;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!imsic ||
|
||||
(imsic->num_ids < IMSIC_MIN_ID) ||
|
||||
(IMSIC_MAX_ID < imsic->num_ids))
|
||||
return SBI_EINVAL;
|
||||
|
||||
tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
|
||||
if (tmp < imsic->guest_index_bits)
|
||||
return SBI_EINVAL;
|
||||
|
||||
tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
|
||||
imsic->guest_index_bits;
|
||||
if (tmp < imsic->hart_index_bits)
|
||||
return SBI_EINVAL;
|
||||
|
||||
tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
|
||||
imsic->guest_index_bits - imsic->hart_index_bits;
|
||||
if (tmp < imsic->group_index_bits)
|
||||
return SBI_EINVAL;
|
||||
|
||||
tmp = IMSIC_MMIO_PAGE_SHIFT + imsic->guest_index_bits +
|
||||
imsic->hart_index_bits;
|
||||
if (imsic->group_index_shift < tmp)
|
||||
return SBI_EINVAL;
|
||||
tmp = imsic->group_index_bits + imsic->group_index_shift - 1;
|
||||
if (tmp >= BITS_PER_LONG)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/*
|
||||
* Number of interrupt identities should be 1 less than
|
||||
* multiple of 63
|
||||
*/
|
||||
if ((imsic->num_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* We should have at least one regset */
|
||||
if (!imsic->regs[0].size)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Match patter of each regset */
|
||||
base_addr = imsic->regs[0].addr;
|
||||
base_addr &= ~((1UL << (imsic->guest_index_bits +
|
||||
imsic->hart_index_bits +
|
||||
IMSIC_MMIO_PAGE_SHIFT)) - 1);
|
||||
base_addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
|
||||
imsic->group_index_shift);
|
||||
for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) {
|
||||
mask = (1UL << imsic->guest_index_bits) * IMSIC_MMIO_PAGE_SZ;
|
||||
mask -= 1UL;
|
||||
if (imsic->regs[i].size & mask)
|
||||
return SBI_EINVAL;
|
||||
|
||||
addr = imsic->regs[i].addr;
|
||||
addr &= ~((1UL << (imsic->guest_index_bits +
|
||||
imsic->hart_index_bits +
|
||||
IMSIC_MMIO_PAGE_SHIFT)) - 1);
|
||||
addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
|
||||
imsic->group_index_shift);
|
||||
if (base_addr != addr)
|
||||
return SBI_EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imsic_cold_irqchip_init(struct imsic_data *imsic)
|
||||
{
|
||||
int i, rc;
|
||||
struct sbi_domain_memregion reg;
|
||||
|
||||
/* Sanity checks */
|
||||
rc = imsic_data_check(imsic);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* We only initialize M-mode IMSIC */
|
||||
if (!imsic->targets_mmode)
|
||||
return SBI_EINVAL;
|
||||
|
||||
/* Setup external interrupt function for IMSIC */
|
||||
sbi_irqchip_set_irqfn(imsic_external_irqfn);
|
||||
|
||||
/* Add IMSIC regions to the root domain */
|
||||
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, ®);
|
||||
rc = sbi_domain_root_add_memregion(®);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Register IPI device */
|
||||
sbi_ipi_set_device(&imsic_ipi_device);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -8,5 +8,17 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += irqchip/fdt_irqchip.o
|
||||
libsbiutils-objs-y += irqchip/fdt_irqchip_drivers.o
|
||||
|
||||
carray-fdt_irqchip_drivers-y += fdt_irqchip_aplic
|
||||
libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o
|
||||
|
||||
carray-fdt_irqchip_drivers-y += fdt_irqchip_imsic
|
||||
libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
|
||||
|
||||
carray-fdt_irqchip_drivers-y += fdt_irqchip_plic
|
||||
libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
|
||||
|
||||
libsbiutils-objs-y += irqchip/aplic.o
|
||||
libsbiutils-objs-y += irqchip/imsic.o
|
||||
libsbiutils-objs-y += irqchip/plic.o
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*
|
||||
* Authors:
|
||||
* Anup Patel <anup.patel@wdc.com>
|
||||
* Samuel Holland <samuel@sholland.org>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
@@ -21,71 +22,138 @@
|
||||
#define PLIC_CONTEXT_BASE 0x200000
|
||||
#define PLIC_CONTEXT_STRIDE 0x1000
|
||||
|
||||
static void plic_set_priority(struct plic_data *plic, u32 source, u32 val)
|
||||
static u32 plic_get_priority(const struct plic_data *plic, u32 source)
|
||||
{
|
||||
volatile void *plic_priority = (void *)plic->addr +
|
||||
volatile void *plic_priority = (char *)plic->addr +
|
||||
PLIC_PRIORITY_BASE + 4 * source;
|
||||
return readl(plic_priority);
|
||||
}
|
||||
|
||||
static void plic_set_priority(const struct plic_data *plic, u32 source, u32 val)
|
||||
{
|
||||
volatile void *plic_priority = (char *)plic->addr +
|
||||
PLIC_PRIORITY_BASE + 4 * source;
|
||||
writel(val, plic_priority);
|
||||
}
|
||||
|
||||
void plic_set_thresh(struct plic_data *plic, u32 cntxid, u32 val)
|
||||
void plic_priority_save(const struct plic_data *plic, u8 *priority)
|
||||
{
|
||||
for (u32 i = 0; i < plic->num_src; i++)
|
||||
priority[i] = plic_get_priority(plic, i);
|
||||
}
|
||||
|
||||
void plic_priority_restore(const struct plic_data *plic, const u8 *priority)
|
||||
{
|
||||
for (u32 i = 0; i < plic->num_src; i++)
|
||||
plic_set_priority(plic, i, priority[i]);
|
||||
}
|
||||
|
||||
static u32 plic_get_thresh(const struct plic_data *plic, u32 cntxid)
|
||||
{
|
||||
volatile void *plic_thresh;
|
||||
|
||||
if (!plic)
|
||||
return;
|
||||
plic_thresh = (char *)plic->addr +
|
||||
PLIC_CONTEXT_BASE + PLIC_CONTEXT_STRIDE * cntxid;
|
||||
|
||||
plic_thresh = (void *)plic->addr +
|
||||
return readl(plic_thresh);
|
||||
}
|
||||
|
||||
static void plic_set_thresh(const struct plic_data *plic, u32 cntxid, u32 val)
|
||||
{
|
||||
volatile void *plic_thresh;
|
||||
|
||||
plic_thresh = (char *)plic->addr +
|
||||
PLIC_CONTEXT_BASE + PLIC_CONTEXT_STRIDE * cntxid;
|
||||
writel(val, plic_thresh);
|
||||
}
|
||||
|
||||
void plic_set_ie(struct plic_data *plic, u32 cntxid, u32 word_index, u32 val)
|
||||
static u32 plic_get_ie(const struct plic_data *plic, u32 cntxid,
|
||||
u32 word_index)
|
||||
{
|
||||
volatile void *plic_ie;
|
||||
|
||||
if (!plic)
|
||||
return;
|
||||
plic_ie = (char *)plic->addr +
|
||||
PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid +
|
||||
4 * word_index;
|
||||
|
||||
plic_ie = (void *)plic->addr +
|
||||
PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid;
|
||||
writel(val, plic_ie + word_index * 4);
|
||||
return readl(plic_ie);
|
||||
}
|
||||
|
||||
int plic_warm_irqchip_init(struct plic_data *plic,
|
||||
int m_cntx_id, int s_cntx_id)
|
||||
static void plic_set_ie(const struct plic_data *plic, u32 cntxid,
|
||||
u32 word_index, u32 val)
|
||||
{
|
||||
size_t i, ie_words;
|
||||
volatile void *plic_ie;
|
||||
|
||||
if (!plic)
|
||||
plic_ie = (char *)plic->addr +
|
||||
PLIC_ENABLE_BASE + PLIC_ENABLE_STRIDE * cntxid +
|
||||
4 * word_index;
|
||||
writel(val, plic_ie);
|
||||
}
|
||||
|
||||
void plic_context_save(const struct plic_data *plic, int context_id,
|
||||
u32 *enable, u32 *threshold)
|
||||
{
|
||||
u32 ie_words = (plic->num_src + 31) / 32;
|
||||
|
||||
for (u32 i = 0; i < ie_words; i++)
|
||||
enable[i] = plic_get_ie(plic, context_id, i);
|
||||
|
||||
*threshold = plic_get_thresh(plic, context_id);
|
||||
}
|
||||
|
||||
void plic_context_restore(const struct plic_data *plic, int context_id,
|
||||
const u32 *enable, u32 threshold)
|
||||
{
|
||||
u32 ie_words = (plic->num_src + 31) / 32;
|
||||
|
||||
for (u32 i = 0; i < ie_words; i++)
|
||||
plic_set_ie(plic, context_id, i, enable[i]);
|
||||
|
||||
plic_set_thresh(plic, context_id, threshold);
|
||||
}
|
||||
|
||||
int plic_context_init(const struct plic_data *plic, int context_id,
|
||||
bool enable, u32 threshold)
|
||||
{
|
||||
u32 ie_words, ie_value;
|
||||
|
||||
if (!plic || context_id < 0)
|
||||
return SBI_EINVAL;
|
||||
|
||||
ie_words = plic->num_src / 32 + 1;
|
||||
ie_words = (plic->num_src + 31) / 32;
|
||||
ie_value = enable ? 0xffffffffU : 0U;
|
||||
|
||||
/* By default, disable all IRQs for M-mode of target HART */
|
||||
if (m_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(plic, m_cntx_id, i, 0);
|
||||
}
|
||||
for (u32 i = 0; i < ie_words; i++)
|
||||
plic_set_ie(plic, context_id, i, ie_value);
|
||||
|
||||
/* By default, disable all IRQs for S-mode of target HART */
|
||||
if (s_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(plic, s_cntx_id, i, 0);
|
||||
}
|
||||
|
||||
/* By default, disable M-mode threshold */
|
||||
if (m_cntx_id > -1)
|
||||
plic_set_thresh(plic, m_cntx_id, 0x7);
|
||||
|
||||
/* By default, disable S-mode threshold */
|
||||
if (s_cntx_id > -1)
|
||||
plic_set_thresh(plic, s_cntx_id, 0x7);
|
||||
plic_set_thresh(plic, context_id, threshold);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_cold_irqchip_init(struct plic_data *plic)
|
||||
int plic_warm_irqchip_init(const struct plic_data *plic,
|
||||
int m_cntx_id, int s_cntx_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* By default, disable all IRQs for M-mode of target HART */
|
||||
if (m_cntx_id > -1) {
|
||||
ret = plic_context_init(plic, m_cntx_id, false, 0x7);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* By default, disable all IRQs for S-mode of target HART */
|
||||
if (s_cntx_id > -1) {
|
||||
ret = plic_context_init(plic, s_cntx_id, false, 0x7);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_cold_irqchip_init(const struct plic_data *plic)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@@ -13,21 +13,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/reset/fdt_reset.h>
|
||||
|
||||
extern struct fdt_reset fdt_poweroff_gpio;
|
||||
extern struct fdt_reset fdt_reset_gpio;
|
||||
extern struct fdt_reset fdt_reset_htif;
|
||||
extern struct fdt_reset fdt_reset_sifive_test;
|
||||
extern struct fdt_reset fdt_reset_sunxi_wdt;
|
||||
extern struct fdt_reset fdt_reset_thead;
|
||||
|
||||
static struct fdt_reset *reset_drivers[] = {
|
||||
&fdt_poweroff_gpio,
|
||||
&fdt_reset_gpio,
|
||||
&fdt_reset_htif,
|
||||
&fdt_reset_sifive_test,
|
||||
&fdt_reset_sunxi_wdt,
|
||||
&fdt_reset_thead,
|
||||
};
|
||||
/* List of FDT reset drivers generated at compile time */
|
||||
extern struct fdt_reset *fdt_reset_drivers[];
|
||||
extern unsigned long fdt_reset_drivers_size;
|
||||
|
||||
int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv)
|
||||
{
|
||||
@@ -54,6 +42,6 @@ void fdt_reset_init(void)
|
||||
int pos;
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(reset_drivers); pos++)
|
||||
fdt_reset_driver_init(fdt, reset_drivers[pos]);
|
||||
for (pos = 0; pos < fdt_reset_drivers_size; pos++)
|
||||
fdt_reset_driver_init(fdt, fdt_reset_drivers[pos]);
|
||||
}
|
||||
|
3
lib/utils/reset/fdt_reset_drivers.carray
Normal file
3
lib/utils/reset/fdt_reset_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/reset/fdt_reset.h
|
||||
TYPE: struct fdt_reset
|
||||
NAME: fdt_reset_drivers
|
@@ -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 = (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 = (void *)TRUE },
|
||||
{ .compatible = "gpio-restart", .data = (const void *)TRUE },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@@ -14,7 +14,17 @@
|
||||
static int htif_reset_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
return htif_system_reset_init();
|
||||
bool custom = false;
|
||||
uint64_t fromhost_addr = 0, tohost_addr = 0;
|
||||
|
||||
if (!fdt_get_node_addr_size(fdt, nodeoff, 0, &fromhost_addr, NULL)) {
|
||||
custom = true;
|
||||
tohost_addr = fromhost_addr + sizeof(uint64_t);
|
||||
}
|
||||
|
||||
fdt_get_node_addr_size(fdt, nodeoff, 1, &tohost_addr, NULL);
|
||||
|
||||
return htif_system_reset_init(custom, fromhost_addr, tohost_addr);
|
||||
}
|
||||
|
||||
static const struct fdt_match htif_reset_match[] = {
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
#define WDT_MODE_REG 0x18
|
||||
|
||||
static volatile void *sunxi_wdt_base;
|
||||
static volatile char *sunxi_wdt_base;
|
||||
|
||||
static int sunxi_wdt_system_reset_check(u32 type, u32 reason)
|
||||
{
|
||||
@@ -59,7 +59,7 @@ static int sunxi_wdt_reset_init(void *fdt, int nodeoff,
|
||||
if (rc < 0 || !reg_addr)
|
||||
return SBI_ENODEV;
|
||||
|
||||
sunxi_wdt_base = (volatile void *)(unsigned long)reg_addr;
|
||||
sunxi_wdt_base = (volatile char *)(unsigned long)reg_addr;
|
||||
|
||||
sbi_system_reset_add_device(&sunxi_wdt_reset);
|
||||
|
||||
|
@@ -59,7 +59,7 @@ extern void __thead_pre_start_warm(void);
|
||||
static int thead_reset_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
void *p;
|
||||
char *p;
|
||||
const fdt64_t *val;
|
||||
const fdt32_t *val_w;
|
||||
int len, i;
|
||||
@@ -91,7 +91,7 @@ static int thead_reset_init(void *fdt, int nodeoff,
|
||||
/* Custom reset method for secondary harts */
|
||||
val = fdt_getprop(fdt, nodeoff, "entry-reg", &len);
|
||||
if (len > 0 && val) {
|
||||
p = (void *)(ulong)fdt64_to_cpu(*val);
|
||||
p = (char *)(ulong)fdt64_to_cpu(*val);
|
||||
|
||||
val_w = fdt_getprop(fdt, nodeoff, "entry-cnt", &len);
|
||||
if (len > 0 && val_w) {
|
||||
|
@@ -8,9 +8,21 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += reset/fdt_reset.o
|
||||
libsbiutils-objs-y += reset/fdt_reset_drivers.o
|
||||
|
||||
carray-fdt_reset_drivers-y += fdt_poweroff_gpio
|
||||
carray-fdt_reset_drivers-y += fdt_reset_gpio
|
||||
libsbiutils-objs-y += reset/fdt_reset_gpio.o
|
||||
|
||||
carray-fdt_reset_drivers-y += fdt_reset_htif
|
||||
libsbiutils-objs-y += reset/fdt_reset_htif.o
|
||||
|
||||
carray-fdt_reset_drivers-y += fdt_reset_sifive_test
|
||||
libsbiutils-objs-y += reset/fdt_reset_sifive_test.o
|
||||
|
||||
carray-fdt_reset_drivers-y += fdt_reset_sunxi_wdt
|
||||
libsbiutils-objs-y += reset/fdt_reset_sunxi_wdt.o
|
||||
|
||||
carray-fdt_reset_drivers-y += fdt_reset_thead
|
||||
libsbiutils-objs-y += reset/fdt_reset_thead.o
|
||||
libsbiutils-objs-y += reset/fdt_reset_thead_asm.o
|
||||
|
@@ -13,21 +13,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/serial/fdt_serial.h>
|
||||
|
||||
extern struct fdt_serial fdt_serial_uart8250;
|
||||
extern struct fdt_serial fdt_serial_sifive;
|
||||
extern struct fdt_serial fdt_serial_litex;
|
||||
extern struct fdt_serial fdt_serial_htif;
|
||||
extern struct fdt_serial fdt_serial_shakti;
|
||||
extern struct fdt_serial fdt_serial_gaisler;
|
||||
|
||||
static struct fdt_serial *serial_drivers[] = {
|
||||
&fdt_serial_uart8250,
|
||||
&fdt_serial_sifive,
|
||||
&fdt_serial_litex,
|
||||
&fdt_serial_htif,
|
||||
&fdt_serial_shakti,
|
||||
&fdt_serial_gaisler
|
||||
};
|
||||
/* List of FDT serial drivers generated at compile time */
|
||||
extern struct fdt_serial *fdt_serial_drivers[];
|
||||
extern unsigned long fdt_serial_drivers_size;
|
||||
|
||||
static struct fdt_serial dummy = {
|
||||
.match_table = NULL,
|
||||
@@ -62,8 +50,8 @@ int fdt_serial_init(void)
|
||||
}
|
||||
|
||||
/* First check DT node pointed by stdout-path */
|
||||
for (pos = 0; pos < array_size(serial_drivers) && -1 < noff; pos++) {
|
||||
drv = serial_drivers[pos];
|
||||
for (pos = 0; pos < fdt_serial_drivers_size && -1 < noff; pos++) {
|
||||
drv = fdt_serial_drivers[pos];
|
||||
|
||||
match = fdt_match_node(fdt, noff, drv->match_table);
|
||||
if (!match)
|
||||
@@ -85,8 +73,8 @@ int fdt_serial_init(void)
|
||||
goto done;
|
||||
|
||||
/* Lastly check all DT nodes */
|
||||
for (pos = 0; pos < array_size(serial_drivers); pos++) {
|
||||
drv = serial_drivers[pos];
|
||||
for (pos = 0; pos < fdt_serial_drivers_size; pos++) {
|
||||
drv = fdt_serial_drivers[pos];
|
||||
|
||||
noff = fdt_find_match(fdt, -1, drv->match_table, &match);
|
||||
if (noff < 0)
|
||||
|
3
lib/utils/serial/fdt_serial_drivers.carray
Normal file
3
lib/utils/serial/fdt_serial_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/serial/fdt_serial.h
|
||||
TYPE: struct fdt_serial
|
||||
NAME: fdt_serial_drivers
|
@@ -19,7 +19,17 @@ static const struct fdt_match serial_htif_match[] = {
|
||||
static int serial_htif_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
return htif_serial_init();
|
||||
bool custom = false;
|
||||
uint64_t fromhost_addr = 0, tohost_addr = 0;
|
||||
|
||||
if (!fdt_get_node_addr_size(fdt, nodeoff, 0, &fromhost_addr, NULL)) {
|
||||
custom = true;
|
||||
tohost_addr = fromhost_addr + sizeof(uint64_t);
|
||||
}
|
||||
|
||||
fdt_get_node_addr_size(fdt, nodeoff, 1, &tohost_addr, NULL);
|
||||
|
||||
return htif_serial_init(custom, fromhost_addr, tohost_addr);
|
||||
}
|
||||
|
||||
struct fdt_serial fdt_serial_htif = {
|
||||
|
@@ -22,7 +22,8 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
|
||||
return rc;
|
||||
|
||||
return uart8250_init(uart.addr, uart.freq, uart.baud,
|
||||
uart.reg_shift, uart.reg_io_width);
|
||||
uart.reg_shift, uart.reg_io_width,
|
||||
uart.reg_offset);
|
||||
}
|
||||
|
||||
static const struct fdt_match serial_uart8250_match[] = {
|
||||
|
35
lib/utils/serial/fdt_serial_xlnx_uartlite.c
Normal file
35
lib/utils/serial/fdt_serial_xlnx_uartlite.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Alistair Francis <alistair.francis@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/serial/fdt_serial.h>
|
||||
#include <sbi_utils/serial/xlnx_uartlite.h>
|
||||
|
||||
static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int rc;
|
||||
struct platform_uart_data uart;
|
||||
|
||||
rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return xlnx_uartlite_init(uart.addr);
|
||||
}
|
||||
|
||||
static const struct fdt_match serial_xlnx_uartlite_match[] = {
|
||||
{ .compatible = "xlnx,xps-uartlite-1.00.a" },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct fdt_serial fdt_serial_xlnx_uartlite = {
|
||||
.match_table = serial_xlnx_uartlite_match,
|
||||
.init = serial_xlnx_uartlite_init,
|
||||
};
|
@@ -29,7 +29,7 @@
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static volatile void *uart_base;
|
||||
static volatile char *uart_base;
|
||||
|
||||
static u32 get_reg(u32 num)
|
||||
{
|
||||
@@ -67,7 +67,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
uart_base = (volatile void *)base;
|
||||
uart_base = (volatile char *)base;
|
||||
|
||||
/* Configure baudrate */
|
||||
if (in_freq)
|
||||
|
@@ -8,14 +8,32 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += serial/fdt_serial.o
|
||||
libsbiutils-objs-y += serial/fdt_serial_drivers.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_gaisler
|
||||
libsbiutils-objs-y += serial/fdt_serial_gaisler.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_htif
|
||||
libsbiutils-objs-y += serial/fdt_serial_htif.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_shakti
|
||||
libsbiutils-objs-y += serial/fdt_serial_shakti.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_sifive
|
||||
libsbiutils-objs-y += serial/fdt_serial_sifive.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_litex
|
||||
libsbiutils-objs-y += serial/fdt_serial_litex.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_uart8250
|
||||
libsbiutils-objs-y += serial/fdt_serial_uart8250.o
|
||||
|
||||
carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
|
||||
libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
|
||||
|
||||
libsbiutils-objs-y += serial/gaisler-uart.o
|
||||
libsbiutils-objs-y += serial/shakti-uart.o
|
||||
libsbiutils-objs-y += serial/sifive-uart.o
|
||||
libsbiutils-objs-y += serial/litex-uart.o
|
||||
libsbiutils-objs-y += serial/uart8250.o
|
||||
libsbiutils-objs-y += serial/xlnx-uartlite.o
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#define UART_TX_FULL 0x2
|
||||
#define UART_RX_FULL 0x8
|
||||
|
||||
static volatile void *uart_base;
|
||||
static volatile char *uart_base;
|
||||
|
||||
static void shakti_uart_putc(char ch)
|
||||
{
|
||||
@@ -46,7 +46,7 @@ static struct sbi_console_device shakti_console = {
|
||||
|
||||
int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
|
||||
{
|
||||
uart_base = (volatile void *)base;
|
||||
uart_base = (volatile char *)base;
|
||||
u16 baud = (u16)(in_freq/(16 * baudrate));
|
||||
writew(baud, uart_base + REG_BAUD);
|
||||
|
||||
|
@@ -29,7 +29,7 @@
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static volatile void *uart_base;
|
||||
static volatile char *uart_base;
|
||||
static u32 uart_in_freq;
|
||||
static u32 uart_baudrate;
|
||||
|
||||
@@ -90,7 +90,7 @@ static struct sbi_console_device sifive_console = {
|
||||
|
||||
int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
|
||||
{
|
||||
uart_base = (volatile void *)base;
|
||||
uart_base = (volatile char *)base;
|
||||
uart_in_freq = in_freq;
|
||||
uart_baudrate = baudrate;
|
||||
|
||||
|
@@ -39,7 +39,7 @@
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static volatile void *uart8250_base;
|
||||
static volatile char *uart8250_base;
|
||||
static u32 uart8250_in_freq;
|
||||
static u32 uart8250_baudrate;
|
||||
static u32 uart8250_reg_width;
|
||||
@@ -91,17 +91,17 @@ static struct sbi_console_device uart8250_console = {
|
||||
};
|
||||
|
||||
int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
|
||||
u32 reg_width)
|
||||
u32 reg_width, u32 reg_offset)
|
||||
{
|
||||
u16 bdiv;
|
||||
|
||||
uart8250_base = (volatile void *)base;
|
||||
uart8250_base = (volatile char *)base + reg_offset;
|
||||
uart8250_reg_shift = reg_shift;
|
||||
uart8250_reg_width = reg_width;
|
||||
uart8250_in_freq = in_freq;
|
||||
uart8250_baudrate = baudrate;
|
||||
|
||||
bdiv = uart8250_in_freq / (16 * uart8250_baudrate);
|
||||
bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
|
||||
|
||||
/* Disable all interrupts */
|
||||
set_reg(UART_IER_OFFSET, 0x00);
|
||||
|
67
lib/utils/serial/xlnx-uartlite.c
Normal file
67
lib/utils/serial/xlnx-uartlite.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Western Digital Corporation or its affiliates.
|
||||
*
|
||||
* Authors:
|
||||
* Alistair Francis <alistair.francis@wdc.com>
|
||||
*/
|
||||
|
||||
#include <sbi/riscv_io.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi_utils/serial/xlnx_uartlite.h>
|
||||
|
||||
/* clang-format off */
|
||||
|
||||
#define UART_RX_OFFSET 0x00
|
||||
#define UART_TX_OFFSET 0x04
|
||||
#define UART_STATUS_OFFSET 0x08
|
||||
# define UART_STATUS_RXVALID 0x01
|
||||
# define UART_STATUS_RXFULL 0x02
|
||||
# define UART_STATUS_TXEMPTY 0x04
|
||||
# define UART_STATUS_TXFULL 0x08
|
||||
# define UART_STATUS_IE 0x10
|
||||
# define UART_STATUS_OVERRUN 0x20
|
||||
# define UART_STATUS_FRAME 0x40
|
||||
# define UART_STATUS_PARITY 0x80
|
||||
#define UART_CTRL_OFFSET 0x0C
|
||||
# define UART_CTRL_RST_TX 0x01
|
||||
# define UART_CTRL_RST_RX 0x02
|
||||
# define UART_CTRL_IE 0x10
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
static volatile char *xlnx_uartlite_base;
|
||||
|
||||
static void xlnx_uartlite_putc(char ch)
|
||||
{
|
||||
while((readb(xlnx_uartlite_base + UART_STATUS_OFFSET) & UART_STATUS_TXFULL))
|
||||
;
|
||||
|
||||
writeb(ch, xlnx_uartlite_base + UART_TX_OFFSET);
|
||||
}
|
||||
|
||||
static int xlnx_uartlite_getc(void)
|
||||
{
|
||||
u16 status = readb(xlnx_uartlite_base + UART_STATUS_OFFSET);
|
||||
|
||||
if (status & UART_STATUS_RXVALID)
|
||||
return readb(xlnx_uartlite_base + UART_RX_OFFSET);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct sbi_console_device xlnx_uartlite_console = {
|
||||
.name = "xlnx-uartlite",
|
||||
.console_putc = xlnx_uartlite_putc,
|
||||
.console_getc = xlnx_uartlite_getc
|
||||
};
|
||||
|
||||
int xlnx_uartlite_init(unsigned long base)
|
||||
{
|
||||
xlnx_uartlite_base = (volatile char *)base;
|
||||
|
||||
sbi_console_set_device(&xlnx_uartlite_console);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <sbi/riscv_locks.h>
|
||||
#include <sbi/sbi_console.h>
|
||||
#include <sbi/sbi_error.h>
|
||||
#include <sbi/sbi_system.h>
|
||||
#include <sbi_utils/sys/htif.h>
|
||||
|
||||
@@ -47,15 +48,46 @@
|
||||
|
||||
volatile uint64_t tohost __attribute__((section(".htif")));
|
||||
volatile uint64_t fromhost __attribute__((section(".htif")));
|
||||
|
||||
static uint64_t *htif_fromhost = NULL;
|
||||
static uint64_t *htif_tohost = NULL;
|
||||
static bool htif_custom = false;
|
||||
|
||||
static int htif_console_buf;
|
||||
static spinlock_t htif_lock = SPIN_LOCK_INITIALIZER;
|
||||
|
||||
static inline uint64_t __read_tohost(void)
|
||||
{
|
||||
return (htif_custom) ? *htif_tohost : tohost;
|
||||
}
|
||||
|
||||
static inline void __write_tohost(uint64_t val)
|
||||
{
|
||||
if (htif_custom)
|
||||
*htif_tohost = val;
|
||||
else
|
||||
tohost = val;
|
||||
}
|
||||
|
||||
static inline uint64_t __read_fromhost(void)
|
||||
{
|
||||
return (htif_custom) ? *htif_fromhost : fromhost;
|
||||
}
|
||||
|
||||
static inline void __write_fromhost(uint64_t val)
|
||||
{
|
||||
if (htif_custom)
|
||||
*htif_fromhost = val;
|
||||
else
|
||||
fromhost = val;
|
||||
}
|
||||
|
||||
static void __check_fromhost()
|
||||
{
|
||||
uint64_t fh = fromhost;
|
||||
uint64_t fh = __read_fromhost();
|
||||
if (!fh)
|
||||
return;
|
||||
fromhost = 0;
|
||||
__write_fromhost(0);
|
||||
|
||||
/* this should be from the console */
|
||||
if (FROMHOST_DEV(fh) != HTIF_DEV_CONSOLE)
|
||||
@@ -73,9 +105,26 @@ static void __check_fromhost()
|
||||
|
||||
static void __set_tohost(uint64_t dev, uint64_t cmd, uint64_t data)
|
||||
{
|
||||
while (tohost)
|
||||
while (__read_tohost())
|
||||
__check_fromhost();
|
||||
tohost = TOHOST_CMD(dev, cmd, data);
|
||||
__write_tohost(TOHOST_CMD(dev, cmd, data));
|
||||
}
|
||||
|
||||
static int set_custom_addr(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr)
|
||||
{
|
||||
if (custom_addr) {
|
||||
if (htif_custom &&
|
||||
((custom_fromhost_addr != (unsigned long)htif_fromhost) ||
|
||||
(custom_tohost_addr != (unsigned long)htif_tohost)))
|
||||
return SBI_EINVAL;
|
||||
htif_fromhost = (uint64_t *)custom_fromhost_addr;
|
||||
htif_tohost = (uint64_t *)custom_tohost_addr;
|
||||
htif_custom = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
@@ -148,10 +197,18 @@ static struct sbi_console_device htif_console = {
|
||||
.console_getc = htif_getc
|
||||
};
|
||||
|
||||
int htif_serial_init(void)
|
||||
int htif_serial_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr)
|
||||
{
|
||||
sbi_console_set_device(&htif_console);
|
||||
int rc;
|
||||
|
||||
rc = set_custom_addr(custom_addr, custom_fromhost_addr,
|
||||
custom_tohost_addr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sbi_console_set_device(&htif_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -163,8 +220,8 @@ static int htif_system_reset_check(u32 type, u32 reason)
|
||||
static void htif_system_reset(u32 type, u32 reason)
|
||||
{
|
||||
while (1) {
|
||||
fromhost = 0;
|
||||
tohost = 1;
|
||||
__write_fromhost(0);
|
||||
__write_tohost(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,9 +231,17 @@ static struct sbi_system_reset_device htif_reset = {
|
||||
.system_reset = htif_system_reset
|
||||
};
|
||||
|
||||
int htif_system_reset_init(void)
|
||||
int htif_system_reset_init(bool custom_addr,
|
||||
unsigned long custom_fromhost_addr,
|
||||
unsigned long custom_tohost_addr)
|
||||
{
|
||||
sbi_system_reset_add_device(&htif_reset);
|
||||
int rc;
|
||||
|
||||
rc = set_custom_addr(custom_addr, custom_fromhost_addr,
|
||||
custom_tohost_addr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sbi_system_reset_add_device(&htif_reset);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ static u64 mtimer_time_rd32(volatile u64 *addr)
|
||||
static void mtimer_time_wr32(bool timecmp, u64 value, volatile u64 *addr)
|
||||
{
|
||||
writel_relaxed((timecmp) ? -1U : 0U, (void *)(addr));
|
||||
writel_relaxed((u32)(value >> 32), (void *)(addr) + 0x04);
|
||||
writel_relaxed((u32)(value >> 32), (char *)(addr) + 0x04);
|
||||
writel_relaxed((u32)value, (void *)(addr));
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ static int aclint_mtimer_add_regions(unsigned long addr, unsigned long size)
|
||||
while (pos < end) {
|
||||
rsize = pos & (MTIMER_ADD_REGION_ALIGN - 1);
|
||||
if (rsize)
|
||||
rsize = 1UL << __ffs(pos);
|
||||
rsize = 1UL << sbi_ffs(pos);
|
||||
else
|
||||
rsize = ((end - pos) < MTIMER_ADD_REGION_ALIGN) ?
|
||||
(end - pos) : MTIMER_ADD_REGION_ALIGN;
|
||||
|
@@ -12,11 +12,9 @@
|
||||
#include <sbi_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/timer/fdt_timer.h>
|
||||
|
||||
extern struct fdt_timer fdt_timer_mtimer;
|
||||
|
||||
static struct fdt_timer *timer_drivers[] = {
|
||||
&fdt_timer_mtimer
|
||||
};
|
||||
/* List of FDT timer drivers generated at compile time */
|
||||
extern struct fdt_timer *fdt_timer_drivers[];
|
||||
extern unsigned long fdt_timer_drivers_size;
|
||||
|
||||
static struct fdt_timer dummy = {
|
||||
.match_table = NULL,
|
||||
@@ -47,8 +45,8 @@ static int fdt_timer_cold_init(void)
|
||||
const struct fdt_match *match;
|
||||
void *fdt = fdt_get_address();
|
||||
|
||||
for (pos = 0; pos < array_size(timer_drivers); pos++) {
|
||||
drv = timer_drivers[pos];
|
||||
for (pos = 0; pos < fdt_timer_drivers_size; pos++) {
|
||||
drv = fdt_timer_drivers[pos];
|
||||
|
||||
noff = -1;
|
||||
while ((noff = fdt_find_match(fdt, noff,
|
||||
|
3
lib/utils/timer/fdt_timer_drivers.carray
Normal file
3
lib/utils/timer/fdt_timer_drivers.carray
Normal file
@@ -0,0 +1,3 @@
|
||||
HEADER: sbi_utils/timer/fdt_timer.h
|
||||
TYPE: struct fdt_timer
|
||||
NAME: fdt_timer_drivers
|
@@ -15,6 +15,11 @@
|
||||
|
||||
#define MTIMER_MAX_NR 16
|
||||
|
||||
struct timer_mtimer_quirks {
|
||||
unsigned int mtime_offset;
|
||||
bool has_64bit_mmio;
|
||||
};
|
||||
|
||||
static unsigned long mtimer_count = 0;
|
||||
static struct aclint_mtimer_data mtimer[MTIMER_MAX_NR];
|
||||
static struct aclint_mtimer_data *mt_reference = NULL;
|
||||
@@ -23,7 +28,7 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff,
|
||||
const struct fdt_match *match)
|
||||
{
|
||||
int i, rc;
|
||||
unsigned long offset, addr[2], size[2];
|
||||
unsigned long addr[2], size[2];
|
||||
struct aclint_mtimer_data *mt;
|
||||
|
||||
if (MTIMER_MAX_NR <= mtimer_count)
|
||||
@@ -43,28 +48,25 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff,
|
||||
return rc;
|
||||
|
||||
if (match->data) { /* SiFive CLINT */
|
||||
const struct timer_mtimer_quirks *quirks = match->data;
|
||||
|
||||
/* Set CLINT addresses */
|
||||
mt->mtimecmp_addr = addr[0] + ACLINT_DEFAULT_MTIMECMP_OFFSET;
|
||||
mt->mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE;
|
||||
mt->mtime_addr = addr[0] + ACLINT_DEFAULT_MTIME_OFFSET;
|
||||
mt->mtime_size = size[0] - mt->mtimecmp_size;
|
||||
/* Adjust MTIMER address and size for CLINT device */
|
||||
offset = *((unsigned long *)match->data);
|
||||
mt->mtime_addr += offset;
|
||||
mt->mtimecmp_addr += offset;
|
||||
mt->mtime_size -= offset;
|
||||
/* Parse additional CLINT properties */
|
||||
if (fdt_getprop(fdt, nodeoff, "clint,has-no-64bit-mmio", &rc))
|
||||
mt->has_64bit_mmio = false;
|
||||
mt->mtime_addr += quirks->mtime_offset;
|
||||
mt->mtimecmp_addr += quirks->mtime_offset;
|
||||
mt->mtime_size -= quirks->mtime_offset;
|
||||
/* Apply additional CLINT quirks */
|
||||
mt->has_64bit_mmio = quirks->has_64bit_mmio;
|
||||
} else { /* RISC-V ACLINT MTIMER */
|
||||
/* Set ACLINT MTIMER addresses */
|
||||
mt->mtime_addr = addr[0];
|
||||
mt->mtime_size = size[0];
|
||||
mt->mtimecmp_addr = addr[1];
|
||||
mt->mtimecmp_size = size[1];
|
||||
/* Parse additional ACLINT MTIMER properties */
|
||||
if (fdt_getprop(fdt, nodeoff, "mtimer,no-64bit-mmio", &rc))
|
||||
mt->has_64bit_mmio = false;
|
||||
}
|
||||
|
||||
/* Check if MTIMER device has shared MTIME address */
|
||||
@@ -107,11 +109,14 @@ static int timer_mtimer_cold_init(void *fdt, int nodeoff,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clint_offset = CLINT_MTIMER_OFFSET;
|
||||
static const struct timer_mtimer_quirks sifive_clint_quirks = {
|
||||
.mtime_offset = CLINT_MTIMER_OFFSET,
|
||||
.has_64bit_mmio = true,
|
||||
};
|
||||
|
||||
static const struct fdt_match timer_mtimer_match[] = {
|
||||
{ .compatible = "riscv,clint0", .data = &clint_offset },
|
||||
{ .compatible = "sifive,clint0", .data = &clint_offset },
|
||||
{ .compatible = "riscv,clint0", .data = &sifive_clint_quirks },
|
||||
{ .compatible = "sifive,clint0", .data = &sifive_clint_quirks },
|
||||
{ .compatible = "riscv,aclint-mtimer" },
|
||||
{ },
|
||||
};
|
||||
|
@@ -8,5 +8,9 @@
|
||||
#
|
||||
|
||||
libsbiutils-objs-y += timer/aclint_mtimer.o
|
||||
|
||||
libsbiutils-objs-y += timer/fdt_timer.o
|
||||
libsbiutils-objs-y += timer/fdt_timer_drivers.o
|
||||
|
||||
carray-fdt_timer_drivers-y += fdt_timer_mtimer
|
||||
libsbiutils-objs-y += timer/fdt_timer_mtimer.o
|
||||
|
@@ -69,7 +69,8 @@ static int ae350_console_init(void)
|
||||
AE350_UART_FREQUENCY,
|
||||
AE350_UART_BAUDRATE,
|
||||
AE350_UART_REG_SHIFT,
|
||||
AE350_UART_REG_WIDTH);
|
||||
AE350_UART_REG_WIDTH,
|
||||
AE350_UART_REG_OFFSET);
|
||||
}
|
||||
|
||||
/* Initialize the platform interrupt controller for current HART. */
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#define AE350_UART_BAUDRATE 38400
|
||||
#define AE350_UART_REG_SHIFT 2
|
||||
#define AE350_UART_REG_WIDTH 0
|
||||
#define AE350_UART_REG_OFFSET 0
|
||||
|
||||
/*Memory and Miscellaneous Registers*/
|
||||
#define CSR_MILMB 0x7c0
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#define ARIANE_UART_BAUDRATE 115200
|
||||
#define ARIANE_UART_REG_SHIFT 2
|
||||
#define ARIANE_UART_REG_WIDTH 4
|
||||
#define ARIANE_UART_REG_OFFSET 0
|
||||
#define ARIANE_PLIC_ADDR 0xc000000
|
||||
#define ARIANE_PLIC_NUM_SOURCES 3
|
||||
#define ARIANE_HART_COUNT 1
|
||||
@@ -92,29 +93,27 @@ static int ariane_console_init(void)
|
||||
ARIANE_UART_FREQ,
|
||||
ARIANE_UART_BAUDRATE,
|
||||
ARIANE_UART_REG_SHIFT,
|
||||
ARIANE_UART_REG_WIDTH);
|
||||
ARIANE_UART_REG_WIDTH,
|
||||
ARIANE_UART_REG_OFFSET);
|
||||
}
|
||||
|
||||
static int plic_ariane_warm_irqchip_init(int m_cntx_id, int s_cntx_id)
|
||||
{
|
||||
size_t i, ie_words = ARIANE_PLIC_NUM_SOURCES / 32 + 1;
|
||||
int ret;
|
||||
|
||||
/* By default, enable all IRQs for M-mode of target HART */
|
||||
if (m_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(&plic, m_cntx_id, i, 1);
|
||||
ret = plic_context_init(&plic, m_cntx_id, true, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable all IRQs for S-mode of target HART */
|
||||
if (s_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(&plic, s_cntx_id, i, 1);
|
||||
ret = plic_context_init(&plic, s_cntx_id, true, 0x0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/* By default, enable M-mode threshold */
|
||||
if (m_cntx_id > -1)
|
||||
plic_set_thresh(&plic, m_cntx_id, 1);
|
||||
/* By default, disable S-mode threshold */
|
||||
if (s_cntx_id > -1)
|
||||
plic_set_thresh(&plic, s_cntx_id, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#define OPENPITON_DEFAULT_UART_BAUDRATE 115200
|
||||
#define OPENPITON_DEFAULT_UART_REG_SHIFT 0
|
||||
#define OPENPITON_DEFAULT_UART_REG_WIDTH 1
|
||||
#define OPENPITON_DEFAULT_UART_REG_OFFSET 0
|
||||
#define OPENPITON_DEFAULT_PLIC_ADDR 0xfff1100000
|
||||
#define OPENPITON_DEFAULT_PLIC_NUM_SOURCES 2
|
||||
#define OPENPITON_DEFAULT_HART_COUNT 3
|
||||
@@ -127,29 +128,27 @@ static int openpiton_console_init(void)
|
||||
uart.freq,
|
||||
uart.baud,
|
||||
OPENPITON_DEFAULT_UART_REG_SHIFT,
|
||||
OPENPITON_DEFAULT_UART_REG_WIDTH);
|
||||
OPENPITON_DEFAULT_UART_REG_WIDTH,
|
||||
OPENPITON_DEFAULT_UART_REG_OFFSET);
|
||||
}
|
||||
|
||||
static int plic_openpiton_warm_irqchip_init(int m_cntx_id, int s_cntx_id)
|
||||
{
|
||||
size_t i, ie_words = plic.num_src / 32 + 1;
|
||||
int ret;
|
||||
|
||||
/* By default, enable all IRQs for M-mode of target HART */
|
||||
if (m_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(&plic, m_cntx_id, i, 1);
|
||||
ret = plic_context_init(&plic, m_cntx_id, true, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable all IRQs for S-mode of target HART */
|
||||
if (s_cntx_id > -1) {
|
||||
for (i = 0; i < ie_words; i++)
|
||||
plic_set_ie(&plic, s_cntx_id, i, 1);
|
||||
ret = plic_context_init(&plic, s_cntx_id, true, 0x0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/* By default, enable M-mode threshold */
|
||||
if (m_cntx_id > -1)
|
||||
plic_set_thresh(&plic, m_cntx_id, 1);
|
||||
/* By default, disable S-mode threshold */
|
||||
if (s_cntx_id > -1)
|
||||
plic_set_thresh(&plic, s_cntx_id, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
6
platform/generic/allwinner/objects.mk
Normal file
6
platform/generic/allwinner/objects.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
|
||||
carray-platform_override_modules-y += sun20i_d1
|
||||
platform-objs-y += allwinner/sun20i-d1.o
|
210
platform/generic/allwinner/sun20i-d1.c
Normal file
210
platform/generic/allwinner/sun20i-d1.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 Samuel Holland <samuel@sholland.org>
|
||||
*/
|
||||
|
||||
#include <platform_override.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_utils/fdt/fdt_helper.h>
|
||||
#include <sbi_utils/irqchip/fdt_irqchip_plic.h>
|
||||
|
||||
#define SUN20I_D1_CCU_BASE ((void *)0x02001000)
|
||||
#define SUN20I_D1_RISCV_CFG_BASE ((void *)0x06010000)
|
||||
#define SUN20I_D1_PPU_BASE ((void *)0x07001000)
|
||||
#define SUN20I_D1_PRCM_BASE ((void *)0x07010000)
|
||||
|
||||
/*
|
||||
* CCU
|
||||
*/
|
||||
|
||||
#define CCU_BGR_ENABLE (BIT(16) | BIT(0))
|
||||
|
||||
#define RISCV_CFG_BGR_REG 0xd0c
|
||||
#define PPU_BGR_REG 0x1ac
|
||||
|
||||
/*
|
||||
* CSRs
|
||||
*/
|
||||
|
||||
#define CSR_MXSTATUS 0x7c0
|
||||
#define CSR_MHCR 0x7c1
|
||||
#define CSR_MCOR 0x7c2
|
||||
#define CSR_MHINT 0x7c5
|
||||
|
||||
static unsigned long csr_mxstatus;
|
||||
static unsigned long csr_mhcr;
|
||||
static unsigned long csr_mhint;
|
||||
|
||||
static void sun20i_d1_csr_save(void)
|
||||
{
|
||||
/* Save custom CSRs. */
|
||||
csr_mxstatus = csr_read(CSR_MXSTATUS);
|
||||
csr_mhcr = csr_read(CSR_MHCR);
|
||||
csr_mhint = csr_read(CSR_MHINT);
|
||||
|
||||
/* Flush and disable caches. */
|
||||
csr_write(CSR_MCOR, 0x22);
|
||||
csr_write(CSR_MHCR, 0x0);
|
||||
}
|
||||
|
||||
static void sun20i_d1_csr_restore(void)
|
||||
{
|
||||
/* Invalidate caches and the branch predictor. */
|
||||
csr_write(CSR_MCOR, 0x70013);
|
||||
|
||||
/* Restore custom CSRs, including the cache state. */
|
||||
csr_write(CSR_MXSTATUS, csr_mxstatus);
|
||||
csr_write(CSR_MHCR, csr_mhcr);
|
||||
csr_write(CSR_MHINT, csr_mhint);
|
||||
}
|
||||
|
||||
/*
|
||||
* PLIC
|
||||
*/
|
||||
|
||||
#define PLIC_SOURCES 176
|
||||
#define PLIC_IE_WORDS ((PLIC_SOURCES + 31) / 32)
|
||||
|
||||
static u8 plic_priority[PLIC_SOURCES];
|
||||
static u32 plic_sie[PLIC_IE_WORDS];
|
||||
static u32 plic_threshold;
|
||||
|
||||
static void sun20i_d1_plic_save(void)
|
||||
{
|
||||
fdt_plic_context_save(true, plic_sie, &plic_threshold);
|
||||
fdt_plic_priority_save(plic_priority);
|
||||
}
|
||||
|
||||
static void sun20i_d1_plic_restore(void)
|
||||
{
|
||||
thead_plic_restore();
|
||||
fdt_plic_priority_restore(plic_priority);
|
||||
fdt_plic_context_restore(true, plic_sie, plic_threshold);
|
||||
}
|
||||
|
||||
/*
|
||||
* PPU
|
||||
*/
|
||||
|
||||
#define PPU_PD_ACTIVE_CTRL 0x2c
|
||||
|
||||
static void sun20i_d1_ppu_save(void)
|
||||
{
|
||||
/* Enable MMIO access. Do not assume S-mode leaves the clock enabled. */
|
||||
writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_PRCM_BASE + PPU_BGR_REG);
|
||||
|
||||
/* Activate automatic power-down during the next WFI. */
|
||||
writel_relaxed(1, SUN20I_D1_PPU_BASE + PPU_PD_ACTIVE_CTRL);
|
||||
}
|
||||
|
||||
static void sun20i_d1_ppu_restore(void)
|
||||
{
|
||||
/* Disable automatic power-down. */
|
||||
writel_relaxed(0, SUN20I_D1_PPU_BASE + PPU_PD_ACTIVE_CTRL);
|
||||
}
|
||||
|
||||
/*
|
||||
* RISCV_CFG
|
||||
*/
|
||||
|
||||
#define RESET_ENTRY_LO_REG 0x0004
|
||||
#define RESET_ENTRY_HI_REG 0x0008
|
||||
#define WAKEUP_EN_REG 0x0020
|
||||
#define WAKEUP_MASK_REG(i) (0x0024 + 4 * (i))
|
||||
|
||||
static void sun20i_d1_riscv_cfg_save(void)
|
||||
{
|
||||
/* Enable MMIO access. Do not assume S-mode leaves the clock enabled. */
|
||||
writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_CCU_BASE + RISCV_CFG_BGR_REG);
|
||||
|
||||
/*
|
||||
* Copy the SIE bits to the wakeup registers. D1 has 160 "real"
|
||||
* interrupt sources, numbered 16-175. These are the ones that map to
|
||||
* the wakeup mask registers (the offset is for GIC compatibility). So
|
||||
* copying SIE to the wakeup mask needs some bit manipulation.
|
||||
*/
|
||||
for (int i = 0; i < PLIC_IE_WORDS - 1; i++)
|
||||
writel_relaxed(plic_sie[i] >> 16 | plic_sie[i + 1] << 16,
|
||||
SUN20I_D1_RISCV_CFG_BASE + WAKEUP_MASK_REG(i));
|
||||
|
||||
/* Enable PPU wakeup for interrupts. */
|
||||
writel_relaxed(1, SUN20I_D1_RISCV_CFG_BASE + WAKEUP_EN_REG);
|
||||
}
|
||||
|
||||
static void sun20i_d1_riscv_cfg_restore(void)
|
||||
{
|
||||
/* Disable PPU wakeup for interrupts. */
|
||||
writel_relaxed(0, SUN20I_D1_RISCV_CFG_BASE + WAKEUP_EN_REG);
|
||||
}
|
||||
|
||||
static void sun20i_d1_riscv_cfg_init(void)
|
||||
{
|
||||
unsigned long entry = sbi_hartid_to_scratch(0)->warmboot_addr;
|
||||
|
||||
/* Enable MMIO access. */
|
||||
writel_relaxed(CCU_BGR_ENABLE, SUN20I_D1_CCU_BASE + RISCV_CFG_BGR_REG);
|
||||
|
||||
/* Program the reset entry address. */
|
||||
writel_relaxed((u32)entry, SUN20I_D1_RISCV_CFG_BASE + RESET_ENTRY_LO_REG);
|
||||
writel_relaxed((u64)entry >> 32, SUN20I_D1_RISCV_CFG_BASE + RESET_ENTRY_HI_REG);
|
||||
}
|
||||
|
||||
static int sun20i_d1_hart_suspend(u32 suspend_type)
|
||||
{
|
||||
/* Use the generic code for retentive suspend. */
|
||||
if (!(suspend_type & SBI_HSM_SUSP_NON_RET_BIT))
|
||||
return SBI_ENOTSUPP;
|
||||
|
||||
sun20i_d1_plic_save();
|
||||
sun20i_d1_ppu_save();
|
||||
sun20i_d1_riscv_cfg_save();
|
||||
sun20i_d1_csr_save();
|
||||
|
||||
/*
|
||||
* If no interrupt is pending, this will power down the CPU power
|
||||
* domain. Otherwise, this will fall through, and the generic HSM
|
||||
* code will jump to the resume address.
|
||||
*/
|
||||
wfi();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sun20i_d1_hart_resume(void)
|
||||
{
|
||||
sun20i_d1_csr_restore();
|
||||
sun20i_d1_riscv_cfg_restore();
|
||||
sun20i_d1_ppu_restore();
|
||||
sun20i_d1_plic_restore();
|
||||
}
|
||||
|
||||
static const struct sbi_hsm_device sun20i_d1_ppu = {
|
||||
.name = "sun20i-d1-ppu",
|
||||
.hart_suspend = sun20i_d1_hart_suspend,
|
||||
.hart_resume = sun20i_d1_hart_resume,
|
||||
};
|
||||
|
||||
static int sun20i_d1_final_init(bool cold_boot, const struct fdt_match *match)
|
||||
{
|
||||
if (cold_boot) {
|
||||
sun20i_d1_riscv_cfg_init();
|
||||
sbi_hsm_set_device(&sun20i_d1_ppu);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fdt_match sun20i_d1_match[] = {
|
||||
{ .compatible = "allwinner,sun20i-d1" },
|
||||
{ },
|
||||
};
|
||||
|
||||
const struct platform_override sun20i_d1 = {
|
||||
.match_table = sun20i_d1_match,
|
||||
.final_init = sun20i_d1_final_init,
|
||||
};
|
@@ -11,6 +11,7 @@
|
||||
#define __PLATFORM_OVERRIDE_H__
|
||||
|
||||
#include <sbi/sbi_types.h>
|
||||
#include <sbi/sbi_trap.h>
|
||||
|
||||
struct platform_override {
|
||||
const struct fdt_match *match_table;
|
||||
@@ -21,6 +22,12 @@ struct platform_override {
|
||||
void (*early_exit)(const struct fdt_match *match);
|
||||
void (*final_exit)(const struct fdt_match *match);
|
||||
int (*fdt_fixup)(void *fdt, const struct fdt_match *match);
|
||||
int (*vendor_ext_check)(long extid, const struct fdt_match *match);
|
||||
int (*vendor_ext_provider)(long extid, long funcid,
|
||||
const struct sbi_trap_regs *regs,
|
||||
unsigned long *out_value,
|
||||
struct sbi_trap_info *out_trap,
|
||||
const struct fdt_match *match);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -8,5 +8,4 @@
|
||||
#
|
||||
|
||||
platform-objs-y += platform.o
|
||||
platform-objs-y += sifive_fu540.o
|
||||
platform-objs-y += sifive_fu740.o
|
||||
platform-objs-y += platform_override_modules.o
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user