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 = get_acpi_id_for_cpu(cpu); 48 49 BUG_ON(acpi_disabled); 50 51 if (!table) { 52 rhct = acpi_get_rhct(); 53 if (!rhct) 54 return -ENOENT; 55 } else { 56 rhct = (struct acpi_table_rhct *)table; 57 } 58 59 end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length); 60 61 for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset); 62 node < end; 63 node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) { 64 if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) { 65 hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr); 66 hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo); 67 if (acpi_cpu_id != hart_info->uid) 68 continue; 69 70 for (int i = 0; i < hart_info->num_offsets; i++) { 71 ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header, 72 rhct, hart_info_node_offset[i]); 73 if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) { 74 isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string, 75 ref_node, size_hdr); 76 *isa = isa_node->isa; 77 return 0; 78 } 79 } 80 } 81 } 82 83 return -1; 84 } 85 86 static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct, 87 struct acpi_rhct_hart_info *hart_info, 88 u32 *cbom_size, u32 *cboz_size, u32 *cbop_size) 89 { 90 u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info); 91 u32 size_hdr = sizeof(struct acpi_rhct_node_header); 92 struct acpi_rhct_node_header *ref_node; 93 struct acpi_rhct_cmo_node *cmo_node; 94 u32 *hart_info_node_offset; 95 96 hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo); 97 for (int i = 0; i < hart_info->num_offsets; i++) { 98 ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header, 99 rhct, hart_info_node_offset[i]); 100 if (ref_node->type == ACPI_RHCT_NODE_TYPE_CMO) { 101 cmo_node = ACPI_ADD_PTR(struct acpi_rhct_cmo_node, 102 ref_node, size_hdr); 103 if (cbom_size && cmo_node->cbom_size <= 30) { 104 if (!*cbom_size) 105 *cbom_size = BIT(cmo_node->cbom_size); 106 else if (*cbom_size != BIT(cmo_node->cbom_size)) 107 pr_warn("CBOM size is not the same across harts\n"); 108 } 109 110 if (cboz_size && cmo_node->cboz_size <= 30) { 111 if (!*cboz_size) 112 *cboz_size = BIT(cmo_node->cboz_size); 113 else if (*cboz_size != BIT(cmo_node->cboz_size)) 114 pr_warn("CBOZ size is not the same across harts\n"); 115 } 116 117 if (cbop_size && cmo_node->cbop_size <= 30) { 118 if (!*cbop_size) 119 *cbop_size = BIT(cmo_node->cbop_size); 120 else if (*cbop_size != BIT(cmo_node->cbop_size)) 121 pr_warn("CBOP size is not the same across harts\n"); 122 } 123 } 124 } 125 } 126 127 /* 128 * During early boot, the caller should call acpi_get_table() and pass its pointer to 129 * these functions (and free up later). At run time, since this table can be used 130 * multiple times, pass NULL so that the table remains in memory. 131 */ 132 void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size, 133 u32 *cboz_size, u32 *cbop_size) 134 { 135 u32 size_hdr = sizeof(struct acpi_rhct_node_header); 136 struct acpi_rhct_node_header *node, *end; 137 struct acpi_rhct_hart_info *hart_info; 138 struct acpi_table_rhct *rhct; 139 140 if (acpi_disabled) 141 return; 142 143 if (table) { 144 rhct = (struct acpi_table_rhct *)table; 145 } else { 146 rhct = acpi_get_rhct(); 147 if (!rhct) 148 return; 149 } 150 151 if (cbom_size) 152 *cbom_size = 0; 153 154 if (cboz_size) 155 *cboz_size = 0; 156 157 if (cbop_size) 158 *cbop_size = 0; 159 160 end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length); 161 for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset); 162 node < end; 163 node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) { 164 if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) { 165 hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr); 166 acpi_parse_hart_info_cmo_node(rhct, hart_info, cbom_size, 167 cboz_size, cbop_size); 168 } 169 } 170 } 171