blob: 0a6c1e37c5a84638f8362ac37f1be0be7b602f68 [file] [log] [blame]
Simon Glass61cc9332020-07-07 13:11:42 -06001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Generation of ACPI (Advanced Configuration and Power Interface) tables
4 *
5 * Copyright 2019 Google LLC
6 * Mostly taken from coreboot
7 */
8
9#define LOG_CATEGORY LOGC_ACPI
10
11#include <common.h>
12#include <dm.h>
Simon Glass7e148f22020-07-07 13:11:50 -060013#include <log.h>
Simon Glass61cc9332020-07-07 13:11:42 -060014#include <acpi/acpigen.h>
15#include <dm/acpi.h>
16
17u8 *acpigen_get_current(struct acpi_ctx *ctx)
18{
19 return ctx->current;
20}
21
22void acpigen_emit_byte(struct acpi_ctx *ctx, uint data)
23{
24 *(u8 *)ctx->current++ = data;
25}
26
27void acpigen_emit_word(struct acpi_ctx *ctx, uint data)
28{
29 acpigen_emit_byte(ctx, data & 0xff);
30 acpigen_emit_byte(ctx, (data >> 8) & 0xff);
31}
32
33void acpigen_emit_dword(struct acpi_ctx *ctx, uint data)
34{
35 /* Output the value in little-endian format */
36 acpigen_emit_byte(ctx, data & 0xff);
37 acpigen_emit_byte(ctx, (data >> 8) & 0xff);
38 acpigen_emit_byte(ctx, (data >> 16) & 0xff);
39 acpigen_emit_byte(ctx, (data >> 24) & 0xff);
40}
Simon Glass7fb8da42020-07-07 13:11:45 -060041
Simon Glass7e148f22020-07-07 13:11:50 -060042/*
43 * Maximum length for an ACPI object generated by this code,
44 *
45 * If you need to change this, change acpigen_write_len_f(ctx) and
46 * acpigen_pop_len(ctx)
47 */
48#define ACPIGEN_MAXLEN 0xfffff
49
50void acpigen_write_len_f(struct acpi_ctx *ctx)
51{
52 assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1));
53 ctx->len_stack[ctx->ltop++] = ctx->current;
54 acpigen_emit_byte(ctx, 0);
55 acpigen_emit_byte(ctx, 0);
56 acpigen_emit_byte(ctx, 0);
57}
58
59void acpigen_pop_len(struct acpi_ctx *ctx)
60{
61 int len;
62 char *p;
63
64 assert(ctx->ltop > 0);
65 p = ctx->len_stack[--ctx->ltop];
66 len = ctx->current - (void *)p;
67 assert(len <= ACPIGEN_MAXLEN);
68 /* generate store length for 0xfffff max */
69 p[0] = ACPI_PKG_LEN_3_BYTES | (len & 0xf);
70 p[1] = len >> 4 & 0xff;
71 p[2] = len >> 12 & 0xff;
72}
73
Simon Glass03967ce2020-07-07 13:11:51 -060074char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el)
75{
76 char *p;
77
78 acpigen_emit_byte(ctx, PACKAGE_OP);
79 acpigen_write_len_f(ctx);
80 p = ctx->current;
81 acpigen_emit_byte(ctx, nr_el);
82
83 return p;
84}
85
Simon Glass83b2bd52020-07-07 13:11:52 -060086void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data)
87{
88 acpigen_emit_byte(ctx, BYTE_PREFIX);
89 acpigen_emit_byte(ctx, data & 0xff);
90}
91
92void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data)
93{
94 acpigen_emit_byte(ctx, WORD_PREFIX);
95 acpigen_emit_word(ctx, data);
96}
97
98void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data)
99{
100 acpigen_emit_byte(ctx, DWORD_PREFIX);
101 acpigen_emit_dword(ctx, data);
102}
103
104void acpigen_write_qword(struct acpi_ctx *ctx, u64 data)
105{
106 acpigen_emit_byte(ctx, QWORD_PREFIX);
107 acpigen_emit_dword(ctx, data & 0xffffffff);
108 acpigen_emit_dword(ctx, (data >> 32) & 0xffffffff);
109}
110
111void acpigen_write_zero(struct acpi_ctx *ctx)
112{
113 acpigen_emit_byte(ctx, ZERO_OP);
114}
115
116void acpigen_write_one(struct acpi_ctx *ctx)
117{
118 acpigen_emit_byte(ctx, ONE_OP);
119}
120
121void acpigen_write_integer(struct acpi_ctx *ctx, u64 data)
122{
123 if (data == 0)
124 acpigen_write_zero(ctx);
125 else if (data == 1)
126 acpigen_write_one(ctx);
127 else if (data <= 0xff)
128 acpigen_write_byte(ctx, (unsigned char)data);
129 else if (data <= 0xffff)
130 acpigen_write_word(ctx, (unsigned int)data);
131 else if (data <= 0xffffffff)
132 acpigen_write_dword(ctx, (unsigned int)data);
133 else
134 acpigen_write_qword(ctx, data);
135}
136
Simon Glass7fb8da42020-07-07 13:11:45 -0600137void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size)
138{
139 int i;
140
141 for (i = 0; i < size; i++)
142 acpigen_emit_byte(ctx, data[i]);
143}
144
145void acpigen_emit_string(struct acpi_ctx *ctx, const char *str)
146{
147 acpigen_emit_stream(ctx, str, str ? strlen(str) : 0);
148 acpigen_emit_byte(ctx, '\0');
149}
Simon Glass3df33bd2020-07-07 13:11:53 -0600150
151void acpigen_write_string(struct acpi_ctx *ctx, const char *str)
152{
153 acpigen_emit_byte(ctx, STRING_PREFIX);
154 acpigen_emit_string(ctx, str);
155}
Simon Glass7aed90d2020-07-07 13:11:54 -0600156
157/*
158 * The naming conventions for ACPI namespace names are a bit tricky as
159 * each element has to be 4 chars wide ("All names are a fixed 32 bits.")
160 * and "By convention, when an ASL compiler pads a name shorter than 4
161 * characters, it is done so with trailing underscores ('_')".
162 *
163 * Check sections 5.3, 20.2.2 and 20.4 of ACPI spec 6.3 for details.
164 */
165static void acpigen_emit_simple_namestring(struct acpi_ctx *ctx,
166 const char *name)
167{
168 const char *ptr;
169 int i;
170
171 for (i = 0, ptr = name; i < 4; i++) {
172 if (!*ptr || *ptr == '.')
173 acpigen_emit_byte(ctx, '_');
174 else
175 acpigen_emit_byte(ctx, *ptr++);
176 }
177}
178
179static void acpigen_emit_double_namestring(struct acpi_ctx *ctx,
180 const char *name, int dotpos)
181{
182 acpigen_emit_byte(ctx, DUAL_NAME_PREFIX);
183 acpigen_emit_simple_namestring(ctx, name);
184 acpigen_emit_simple_namestring(ctx, &name[dotpos + 1]);
185}
186
187static void acpigen_emit_multi_namestring(struct acpi_ctx *ctx,
188 const char *name)
189{
190 unsigned char *pathlen;
191 int count = 0;
192
193 acpigen_emit_byte(ctx, MULTI_NAME_PREFIX);
194 pathlen = ctx->current;
195 acpigen_emit_byte(ctx, 0);
196
197 while (*name) {
198 acpigen_emit_simple_namestring(ctx, name);
199 /* find end or next entity */
200 while (*name != '.' && *name)
201 name++;
202 /* forward to next */
203 if (*name == '.')
204 name++;
205 count++;
206 }
207
208 *pathlen = count;
209}
210
211void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath)
212{
213 int dotcount;
214 int dotpos;
215 int i;
216
217 /* We can start with a '\' */
218 if (*namepath == '\\') {
219 acpigen_emit_byte(ctx, '\\');
220 namepath++;
221 }
222
223 /* And there can be any number of '^' */
224 while (*namepath == '^') {
225 acpigen_emit_byte(ctx, '^');
226 namepath++;
227 }
228
229 for (i = 0, dotcount = 0; namepath[i]; i++) {
230 if (namepath[i] == '.') {
231 dotcount++;
232 dotpos = i;
233 }
234 }
235
236 /* If we have only \\ or only ^* then we need to add a null name */
237 if (!*namepath)
238 acpigen_emit_byte(ctx, ZERO_OP);
239 else if (dotcount == 0)
240 acpigen_emit_simple_namestring(ctx, namepath);
241 else if (dotcount == 1)
242 acpigen_emit_double_namestring(ctx, namepath, dotpos);
243 else
244 acpigen_emit_multi_namestring(ctx, namepath);
245}
246
247void acpigen_write_name(struct acpi_ctx *ctx, const char *namepath)
248{
249 acpigen_emit_byte(ctx, NAME_OP);
250 acpigen_emit_namestring(ctx, namepath);
251}