1*69e9ad1bSHuang Borong // SPDX-License-Identifier: BSD-2-Clause
2*69e9ad1bSHuang Borong /*
3*69e9ad1bSHuang Borong * Copyright (c) 2021 Western Digital Corporation or its affiliates.
4*69e9ad1bSHuang Borong * Copyright (c) 2022 Ventana Micro Systems Inc.
5*69e9ad1bSHuang Borong * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC)
6*69e9ad1bSHuang Borong *
7*69e9ad1bSHuang Borong * Authors:
8*69e9ad1bSHuang Borong * Anup Patel <anup.patel@wdc.com>
9*69e9ad1bSHuang Borong * Huang Borong <huangborong@bosc.ac.cn>
10*69e9ad1bSHuang Borong */
11*69e9ad1bSHuang Borong
12*69e9ad1bSHuang Borong #include <drivers/aplic_priv.h>
13*69e9ad1bSHuang Borong #include <drivers/imsic.h>
14*69e9ad1bSHuang Borong #include <dt-bindings/interrupt-controller/irq.h>
15*69e9ad1bSHuang Borong #include <encoding.h>
16*69e9ad1bSHuang Borong #include <io.h>
17*69e9ad1bSHuang Borong #include <kernel/dt.h>
18*69e9ad1bSHuang Borong #include <kernel/dt_driver.h>
19*69e9ad1bSHuang Borong #include <libfdt.h>
20*69e9ad1bSHuang Borong #include <tee_api_types.h>
21*69e9ad1bSHuang Borong #include <trace.h>
22*69e9ad1bSHuang Borong
aplic_parse_fdt_node(const void * fdt,int nodeoff,struct aplic_data * aplic)23*69e9ad1bSHuang Borong static TEE_Result aplic_parse_fdt_node(const void *fdt, int nodeoff,
24*69e9ad1bSHuang Borong struct aplic_data *aplic)
25*69e9ad1bSHuang Borong {
26*69e9ad1bSHuang Borong const fdt32_t *val = NULL;
27*69e9ad1bSHuang Borong struct imsic_data imsic = { };
28*69e9ad1bSHuang Borong int i = 0;
29*69e9ad1bSHuang Borong int len = 0;
30*69e9ad1bSHuang Borong int noff = -1;
31*69e9ad1bSHuang Borong int rc = -1;
32*69e9ad1bSHuang Borong paddr_t reg_addr = 0;
33*69e9ad1bSHuang Borong size_t reg_size = 0;
34*69e9ad1bSHuang Borong TEE_Result res = TEE_ERROR_GENERIC;
35*69e9ad1bSHuang Borong
36*69e9ad1bSHuang Borong if (nodeoff < 0 || !aplic || !fdt)
37*69e9ad1bSHuang Borong return TEE_ERROR_BAD_PARAMETERS;
38*69e9ad1bSHuang Borong
39*69e9ad1bSHuang Borong rc = fdt_reg_info(fdt, nodeoff, ®_addr, ®_size);
40*69e9ad1bSHuang Borong if (rc < 0 || !reg_addr || !reg_size)
41*69e9ad1bSHuang Borong return TEE_ERROR_ITEM_NOT_FOUND;
42*69e9ad1bSHuang Borong aplic->aplic_base = core_mmu_get_va(reg_addr, MEM_AREA_IO_SEC,
43*69e9ad1bSHuang Borong reg_size);
44*69e9ad1bSHuang Borong if (!aplic->aplic_base)
45*69e9ad1bSHuang Borong return TEE_ERROR_GENERIC;
46*69e9ad1bSHuang Borong aplic->size = reg_size;
47*69e9ad1bSHuang Borong
48*69e9ad1bSHuang Borong val = fdt_getprop(fdt, nodeoff, "riscv,num-sources", &len);
49*69e9ad1bSHuang Borong if (len > 0)
50*69e9ad1bSHuang Borong aplic->num_source = fdt32_to_cpu(*val);
51*69e9ad1bSHuang Borong
52*69e9ad1bSHuang Borong val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
53*69e9ad1bSHuang Borong if (val && (size_t)len >= (2 * sizeof(fdt32_t))) {
54*69e9ad1bSHuang Borong len = len / sizeof(fdt32_t);
55*69e9ad1bSHuang Borong for (i = 0; i < len; i += 2) {
56*69e9ad1bSHuang Borong if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
57*69e9ad1bSHuang Borong aplic->targets_mmode = true;
58*69e9ad1bSHuang Borong break;
59*69e9ad1bSHuang Borong }
60*69e9ad1bSHuang Borong }
61*69e9ad1bSHuang Borong aplic->num_idc = len / 2;
62*69e9ad1bSHuang Borong
63*69e9ad1bSHuang Borong return TEE_SUCCESS;
64*69e9ad1bSHuang Borong } else if (!val) {
65*69e9ad1bSHuang Borong val = fdt_getprop(fdt, nodeoff, "msi-parent", &len);
66*69e9ad1bSHuang Borong if (val && (size_t)len >= sizeof(fdt32_t)) {
67*69e9ad1bSHuang Borong noff = fdt_node_offset_by_phandle(fdt,
68*69e9ad1bSHuang Borong fdt32_to_cpu(*val));
69*69e9ad1bSHuang Borong if (noff < 0)
70*69e9ad1bSHuang Borong return TEE_ERROR_ITEM_NOT_FOUND;
71*69e9ad1bSHuang Borong
72*69e9ad1bSHuang Borong res = imisc_parse_fdt_node(fdt, noff, &imsic);
73*69e9ad1bSHuang Borong if (res)
74*69e9ad1bSHuang Borong return res;
75*69e9ad1bSHuang Borong
76*69e9ad1bSHuang Borong aplic->targets_mmode = imsic.targets_mmode;
77*69e9ad1bSHuang Borong
78*69e9ad1bSHuang Borong return TEE_SUCCESS;
79*69e9ad1bSHuang Borong }
80*69e9ad1bSHuang Borong }
81*69e9ad1bSHuang Borong return TEE_ERROR_ITEM_NOT_FOUND;
82*69e9ad1bSHuang Borong }
83*69e9ad1bSHuang Borong
aplic_init_from_device_tree(struct aplic_data * aplic)84*69e9ad1bSHuang Borong TEE_Result aplic_init_from_device_tree(struct aplic_data *aplic)
85*69e9ad1bSHuang Borong {
86*69e9ad1bSHuang Borong void *fdt = NULL;
87*69e9ad1bSHuang Borong int node = FDT_ERR_NOTFOUND;
88*69e9ad1bSHuang Borong TEE_Result res = TEE_ERROR_GENERIC;
89*69e9ad1bSHuang Borong
90*69e9ad1bSHuang Borong fdt = get_dt();
91*69e9ad1bSHuang Borong if (!fdt) {
92*69e9ad1bSHuang Borong EMSG("Unable to get DTB, APLIC init failed");
93*69e9ad1bSHuang Borong return TEE_ERROR_ITEM_NOT_FOUND;
94*69e9ad1bSHuang Borong }
95*69e9ad1bSHuang Borong
96*69e9ad1bSHuang Borong /*
97*69e9ad1bSHuang Borong * Currently, only the S-level interrupt domain is considered.
98*69e9ad1bSHuang Borong * If the interrupt domain is M-level, continue traversing.
99*69e9ad1bSHuang Borong * If it is S-level, return directly.
100*69e9ad1bSHuang Borong */
101*69e9ad1bSHuang Borong node = fdt_node_offset_by_compatible(fdt, -1, APLIC_COMPATIBLE);
102*69e9ad1bSHuang Borong while (node != -FDT_ERR_NOTFOUND) {
103*69e9ad1bSHuang Borong res = aplic_parse_fdt_node(fdt, node, aplic);
104*69e9ad1bSHuang Borong if (res) {
105*69e9ad1bSHuang Borong EMSG("Parse APLIC node failed");
106*69e9ad1bSHuang Borong return res;
107*69e9ad1bSHuang Borong }
108*69e9ad1bSHuang Borong
109*69e9ad1bSHuang Borong if (!aplic->targets_mmode)
110*69e9ad1bSHuang Borong return TEE_SUCCESS;
111*69e9ad1bSHuang Borong
112*69e9ad1bSHuang Borong node = fdt_node_offset_by_compatible(fdt, node,
113*69e9ad1bSHuang Borong APLIC_COMPATIBLE);
114*69e9ad1bSHuang Borong }
115*69e9ad1bSHuang Borong
116*69e9ad1bSHuang Borong return TEE_ERROR_ITEM_NOT_FOUND;
117*69e9ad1bSHuang Borong }
118*69e9ad1bSHuang Borong
aplic_set_source_mode(struct aplic_data * aplic,uint32_t source,uint32_t type)119*69e9ad1bSHuang Borong TEE_Result aplic_set_source_mode(struct aplic_data *aplic, uint32_t source,
120*69e9ad1bSHuang Borong uint32_t type)
121*69e9ad1bSHuang Borong {
122*69e9ad1bSHuang Borong vaddr_t sourcecfg = 0;
123*69e9ad1bSHuang Borong uint32_t sm = APLIC_SOURCECFG_SM_INACTIVE;
124*69e9ad1bSHuang Borong
125*69e9ad1bSHuang Borong switch (type) {
126*69e9ad1bSHuang Borong case IRQ_TYPE_NONE:
127*69e9ad1bSHuang Borong sm = APLIC_SOURCECFG_SM_INACTIVE;
128*69e9ad1bSHuang Borong break;
129*69e9ad1bSHuang Borong case IRQ_TYPE_EDGE_RISING:
130*69e9ad1bSHuang Borong sm = APLIC_SOURCECFG_SM_EDGE_RISE;
131*69e9ad1bSHuang Borong break;
132*69e9ad1bSHuang Borong case IRQ_TYPE_EDGE_FALLING:
133*69e9ad1bSHuang Borong sm = APLIC_SOURCECFG_SM_EDGE_FALL;
134*69e9ad1bSHuang Borong break;
135*69e9ad1bSHuang Borong case IRQ_TYPE_LEVEL_HIGH:
136*69e9ad1bSHuang Borong sm = APLIC_SOURCECFG_SM_LEVEL_HIGH;
137*69e9ad1bSHuang Borong break;
138*69e9ad1bSHuang Borong case IRQ_TYPE_LEVEL_LOW:
139*69e9ad1bSHuang Borong sm = APLIC_SOURCECFG_SM_LEVEL_LOW;
140*69e9ad1bSHuang Borong break;
141*69e9ad1bSHuang Borong default:
142*69e9ad1bSHuang Borong return TEE_ERROR_BAD_PARAMETERS;
143*69e9ad1bSHuang Borong }
144*69e9ad1bSHuang Borong
145*69e9ad1bSHuang Borong sourcecfg = aplic->aplic_base + APLIC_SOURCECFG_BASE +
146*69e9ad1bSHuang Borong (source - 1) * sizeof(uint32_t);
147*69e9ad1bSHuang Borong io_write32(sourcecfg, sm);
148*69e9ad1bSHuang Borong
149*69e9ad1bSHuang Borong return TEE_SUCCESS;
150*69e9ad1bSHuang Borong }
151