1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2022-2023, Ventana Micro Systems Inc 4 * Author: Sunil V L <sunilvl@ventanamicro.com> 5 * 6 */ 7 8 #define pr_fmt(fmt) "ACPI: RHCT: " fmt 9 10 #include <linux/acpi.h> 11 #include <linux/bits.h> 12 13 static struct acpi_table_rhct *acpi_get_rhct(void) 14 { 15 static struct acpi_table_header *rhct; 16 acpi_status status; 17 18 /* 19 * RHCT will be used at runtime on every CPU, so we 20 * don't need to call acpi_put_table() to release the table mapping. 21 */ 22 if (!rhct) { 23 status = acpi_get_table(ACPI_SIG_RHCT, 0, &rhct); 24 if (ACPI_FAILURE(status)) { 25 pr_warn_once("No RHCT table found\n"); 26 return NULL; 27 } 28 } 29 30 return (struct acpi_table_rhct *)rhct; 31 } 32 33 /* 34 * During early boot, the caller should call acpi_get_table() and pass its pointer to 35 * these functions(and free up later). At run time, since this table can be used 36 * multiple times, NULL may be passed in order to use the cached table. 37 */ 38 int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const char **isa) 39 { 40 struct acpi_rhct_node_header *node, *ref_node, *end; 41 u32 size_hdr = sizeof(struct acpi_rhct_node_header); 42 u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info); 43 struct acpi_rhct_hart_info *hart_info; 44 struct acpi_rhct_isa_string *isa_node; 45 struct acpi_table_rhct *rhct; 46 u32 *hart_info_node_offset; 47 u32 acpi_cpu_id; 48 int ret; 49 50 BUG_ON(acpi_disabled); 51 52 ret = acpi_get_cpu_uid(cpu, &acpi_cpu_id); 53 if (ret != 0) 54 return ret; 55 56 if (!table) { 57 rhct = acpi_get_rhct(); 58 if (!rhct) 59 return -ENOENT; 60 } else { 61 rhct = (struct acpi_table_rhct *)table; 62 } 63 64 end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length); 65 66 for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset); 67 node < end; 68 node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) { 69 if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) { 70 hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr); 71 hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo); 72 if (acpi_cpu_id != hart_info->uid) 73 continue; 74 75 for (int i = 0; i < hart_info->num_offsets; i++) { 76 ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header, 77 rhct, hart_info_node_offset[i]); 78 if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) { 79 isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string, 80 ref_node, size_hdr); 81 *isa = isa_node->isa; 82 return 0; 83 } 84 } 85 } 86 } 87 88 return -1; 89 } 90 91 static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct, 92 struct acpi_rhct_hart_info *hart_info, 93 u32 *cbom_size, u32 *cboz_size, u32 *cbop_size) 94 { 95 u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info); 96 u32 size_hdr = sizeof(struct acpi_rhct_node_header); 97 struct acpi_rhct_node_header *ref_node; 98 struct acpi_rhct_cmo_node *cmo_node; 99 u32 *hart_info_node_offset; 100 101 hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo); 102 for (int i = 0; i < hart_info->num_offsets; i++) { 103 ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header, 104 rhct, hart_info_node_offset[i]); 105 if (ref_node->type == ACPI_RHCT_NODE_TYPE_CMO) { 106 cmo_node = ACPI_ADD_PTR(struct acpi_rhct_cmo_node, 107 ref_node, size_hdr); 108 if (cbom_size && cmo_node->cbom_size <= 30) { 109 if (!*cbom_size) 110 *cbom_size = BIT(cmo_node->cbom_size); 111 else if (*cbom_size != BIT(cmo_node->cbom_size)) 112 pr_warn("CBOM size is not the same across harts\n"); 113 } 114 115 if (cboz_size && cmo_node->cboz_size <= 30) { 116 if (!*cboz_size) 117 *cboz_size = BIT(cmo_node->cboz_size); 118 else if (*cboz_size != BIT(cmo_node->cboz_size)) 119 pr_warn("CBOZ size is not the same across harts\n"); 120 } 121 122 if (cbop_size && cmo_node->cbop_size <= 30) { 123 if (!*cbop_size) 124 *cbop_size = BIT(cmo_node->cbop_size); 125 else if (*cbop_size != BIT(cmo_node->cbop_size)) 126 pr_warn("CBOP size is not the same across harts\n"); 127 } 128 } 129 } 130 } 131 132 /* 133 * During early boot, the caller should call acpi_get_table() and pass its pointer to 134 * these functions (and free up later). At run time, since this table can be used 135 * multiple times, pass NULL so that the table remains in memory. 136 */ 137 void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size, 138 u32 *cboz_size, u32 *cbop_size) 139 { 140 u32 size_hdr = sizeof(struct acpi_rhct_node_header); 141 struct acpi_rhct_node_header *node, *end; 142 struct acpi_rhct_hart_info *hart_info; 143 struct acpi_table_rhct *rhct; 144 145 if (acpi_disabled) 146 return; 147 148 if (table) { 149 rhct = (struct acpi_table_rhct *)table; 150 } else { 151 rhct = acpi_get_rhct(); 152 if (!rhct) 153 return; 154 } 155 156 if (cbom_size) 157 *cbom_size = 0; 158 159 if (cboz_size) 160 *cboz_size = 0; 161 162 if (cbop_size) 163 *cbop_size = 0; 164 165 end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length); 166 for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset); 167 node < end; 168 node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) { 169 if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) { 170 hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr); 171 acpi_parse_hart_info_cmo_node(rhct, hart_info, cbom_size, 172 cboz_size, cbop_size); 173 } 174 } 175 } 176