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, 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 | */ |
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 | |