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