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
13static 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, instance: 0, out_table: &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 */
38int 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
86static 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 */
132void 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

source code of linux/drivers/acpi/riscv/rhct.c