1#include <uacpi/internal/registers.h>
2#include <uacpi/internal/stdlib.h>
3#include <uacpi/internal/context.h>
4#include <uacpi/internal/io.h>
5#include <uacpi/acpi.h>
6
7enum register_kind {
8 REGISTER_KIND_GAS,
9 REGISTER_KIND_IO,
10};
11
12enum register_access_kind {
13 REGISTER_ACCESS_KIND_PRESERVE,
14 REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
15 REGISTER_ACCESS_KIND_NORMAL,
16};
17
18struct register_spec {
19 uacpi_u8 kind;
20 uacpi_u8 access_kind;
21 uacpi_u8 access_width; // only REGISTER_KIND_IO
22 void *accessor0, *accessor1;
23 uacpi_u64 write_only_mask;
24 uacpi_u64 preserve_mask;
25};
26
27static struct register_spec registers[UACPI_REGISTER_MAX + 1] = {
28 [UACPI_REGISTER_PM1_STS] = {
29 .kind = REGISTER_KIND_GAS,
30 .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
31 .accessor0 = &g_uacpi_rt_ctx.pm1a_status_blk,
32 .accessor1 = &g_uacpi_rt_ctx.pm1b_status_blk,
33 .preserve_mask = ACPI_PM1_STS_IGN0_MASK,
34 },
35 [UACPI_REGISTER_PM1_EN] = {
36 .kind = REGISTER_KIND_GAS,
37 .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
38 .accessor0 = &g_uacpi_rt_ctx.pm1a_enable_blk,
39 .accessor1 = &g_uacpi_rt_ctx.pm1b_enable_blk,
40 },
41 [UACPI_REGISTER_PM1_CNT] = {
42 .kind = REGISTER_KIND_GAS,
43 .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
44 .accessor0 = &g_uacpi_rt_ctx.fadt.x_pm1a_cnt_blk,
45 .accessor1 = &g_uacpi_rt_ctx.fadt.x_pm1b_cnt_blk,
46 .write_only_mask = ACPI_PM1_CNT_SLP_EN_MASK |
47 ACPI_PM1_CNT_GBL_RLS_MASK,
48 .preserve_mask = ACPI_PM1_CNT_PRESERVE_MASK,
49 },
50 [UACPI_REGISTER_PM_TMR] = {
51 .kind = REGISTER_KIND_GAS,
52 .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
53 .accessor0 = &g_uacpi_rt_ctx.fadt.x_pm_tmr_blk,
54 },
55 [UACPI_REGISTER_PM2_CNT] = {
56 .kind = REGISTER_KIND_GAS,
57 .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
58 .accessor0 = &g_uacpi_rt_ctx.fadt.x_pm2_cnt_blk,
59 .preserve_mask = ACPI_PM2_CNT_PRESERVE_MASK,
60 },
61 [UACPI_REGISTER_SLP_CNT] = {
62 .kind = REGISTER_KIND_GAS,
63 .access_kind = REGISTER_ACCESS_KIND_PRESERVE,
64 .accessor0 = &g_uacpi_rt_ctx.fadt.sleep_control_reg,
65 .write_only_mask = ACPI_SLP_CNT_SLP_EN_MASK,
66 .preserve_mask = ACPI_SLP_CNT_PRESERVE_MASK,
67 },
68 [UACPI_REGISTER_SLP_STS] = {
69 .kind = REGISTER_KIND_GAS,
70 .access_kind = REGISTER_ACCESS_KIND_WRITE_TO_CLEAR,
71 .accessor0 = &g_uacpi_rt_ctx.fadt.sleep_status_reg,
72 .preserve_mask = ACPI_SLP_STS_PRESERVE_MASK,
73 },
74 [UACPI_REGISTER_RESET] = {
75 .kind = REGISTER_KIND_GAS,
76 .access_kind = REGISTER_ACCESS_KIND_NORMAL,
77 .accessor0 = &g_uacpi_rt_ctx.fadt.reset_reg,
78 },
79 [UACPI_REGISTER_SMI_CMD] = {
80 .kind = REGISTER_KIND_IO,
81 .access_kind = REGISTER_ACCESS_KIND_NORMAL,
82 .access_width = 1,
83 .accessor0 = &g_uacpi_rt_ctx.fadt.smi_cmd,
84 },
85};
86
87static struct register_spec *get_reg(uacpi_u8 idx)
88{
89 if (idx > UACPI_REGISTER_MAX)
90 return UACPI_NULL;
91
92 return &registers[idx];
93}
94
95static uacpi_status read_one(
96 enum register_kind kind, void *reg, uacpi_u8 byte_width,
97 uacpi_u64 *out_value
98)
99{
100 if (kind == REGISTER_KIND_GAS) {
101 struct acpi_gas *gas = reg;
102
103 if (!gas->address)
104 return UACPI_STATUS_OK;
105
106 return uacpi_gas_read(gas: reg, value: out_value);
107 }
108
109 return uacpi_kernel_raw_io_read(address: *(uacpi_u32*)reg, byte_width, out_value);
110}
111
112static uacpi_status write_one(
113 enum register_kind kind, void *reg, uacpi_u8 byte_width,
114 uacpi_u64 in_value
115)
116{
117 if (kind == REGISTER_KIND_GAS) {
118 struct acpi_gas *gas = reg;
119
120 if (!gas->address)
121 return UACPI_STATUS_OK;
122
123 return uacpi_gas_write(gas: reg, value: in_value);
124 }
125
126 return uacpi_kernel_raw_io_write(address: *(uacpi_u32*)reg, byte_width, in_value);
127}
128
129static uacpi_status do_read_register(
130 struct register_spec *reg, uacpi_u64 *out_value
131)
132{
133 uacpi_status ret;
134 uacpi_u64 value0, value1 = 0;
135
136 ret = read_one(kind: reg->kind, reg: reg->accessor0, byte_width: reg->access_width, out_value: &value0);
137 if (uacpi_unlikely_error(ret))
138 return ret;
139
140 if (reg->accessor1) {
141 ret = read_one(kind: reg->kind, reg: reg->accessor1, byte_width: reg->access_width, out_value: &value1);
142 if (uacpi_unlikely_error(ret))
143 return ret;
144 }
145
146 *out_value = value0 | value1;
147 if (reg->write_only_mask)
148 *out_value &= ~reg->write_only_mask;
149
150 return UACPI_STATUS_OK;
151}
152
153uacpi_status uacpi_read_register(
154 enum uacpi_register reg_enum, uacpi_u64 *out_value
155)
156{
157 struct register_spec *reg;
158
159 reg = get_reg(idx: reg_enum);
160 if (uacpi_unlikely(reg == UACPI_NULL))
161 return UACPI_STATUS_INVALID_ARGUMENT;
162
163 return do_read_register(reg, out_value);
164}
165
166static uacpi_status do_write_register(
167 struct register_spec *reg, uacpi_u64 in_value
168)
169{
170 uacpi_status ret;
171
172 if (reg->preserve_mask) {
173 in_value &= ~reg->preserve_mask;
174
175 if (reg->access_kind == REGISTER_ACCESS_KIND_PRESERVE) {
176 uacpi_u64 data;
177
178 ret = do_read_register(reg, out_value: &data);
179 if (uacpi_unlikely_error(ret))
180 return ret;
181
182 in_value |= data & reg->preserve_mask;
183 }
184 }
185
186 ret = write_one(kind: reg->kind, reg: reg->accessor0, byte_width: reg->access_width, in_value);
187 if (uacpi_unlikely_error(ret))
188 return ret;
189
190 if (reg->accessor1)
191 ret = write_one(kind: reg->kind, reg: reg->accessor1, byte_width: reg->access_width, in_value);
192
193 return ret;
194}
195
196uacpi_status uacpi_write_register(
197 enum uacpi_register reg_enum, uacpi_u64 in_value
198)
199{
200 struct register_spec *reg;
201
202 reg = get_reg(idx: reg_enum);
203 if (uacpi_unlikely(reg == UACPI_NULL))
204 return UACPI_STATUS_INVALID_ARGUMENT;
205
206 return do_write_register(reg, in_value);
207}
208
209uacpi_status uacpi_write_registers(
210 enum uacpi_register reg_enum, uacpi_u64 in_value0, uacpi_u64 in_value1
211)
212{
213 uacpi_status ret;
214 struct register_spec *reg;
215
216 reg = get_reg(idx: reg_enum);
217 if (uacpi_unlikely(reg == UACPI_NULL))
218 return UACPI_STATUS_INVALID_ARGUMENT;
219
220 ret = write_one(kind: reg->kind, reg: reg->accessor0, byte_width: reg->access_width, in_value: in_value0);
221 if (uacpi_unlikely_error(ret))
222 return ret;
223
224 if (reg->accessor1)
225 ret = write_one(kind: reg->kind, reg: reg->accessor1, byte_width: reg->access_width, in_value: in_value1);
226
227 return ret;
228}
229
230struct register_field {
231 uacpi_u8 reg;
232 uacpi_u8 offset;
233 uacpi_u16 mask;
234};
235
236static struct register_field fields[UACPI_REGISTER_FIELD_MAX + 1] = {
237 [UACPI_REGISTER_FIELD_TMR_STS] = {
238 .reg = UACPI_REGISTER_PM1_STS,
239 .offset = ACPI_PM1_STS_TMR_STS_IDX,
240 .mask = ACPI_PM1_STS_TMR_STS_MASK,
241 },
242 [UACPI_REGISTER_FIELD_BM_STS] = {
243 .reg = UACPI_REGISTER_PM1_STS,
244 .offset = ACPI_PM1_STS_BM_STS_IDX,
245 .mask = ACPI_PM1_STS_BM_STS_MASK,
246 },
247 [UACPI_REGISTER_FIELD_GBL_STS] = {
248 .reg = UACPI_REGISTER_PM1_STS,
249 .offset = ACPI_PM1_STS_GBL_STS_IDX,
250 .mask = ACPI_PM1_STS_GBL_STS_MASK,
251 },
252 [UACPI_REGISTER_FIELD_PWRBTN_STS] = {
253 .reg = UACPI_REGISTER_PM1_STS,
254 .offset = ACPI_PM1_STS_PWRBTN_STS_IDX,
255 .mask = ACPI_PM1_STS_PWRBTN_STS_MASK,
256 },
257 [UACPI_REGISTER_FIELD_SLPBTN_STS] = {
258 .reg = UACPI_REGISTER_PM1_STS,
259 .offset = ACPI_PM1_STS_SLPBTN_STS_IDX,
260 .mask = ACPI_PM1_STS_SLPBTN_STS_MASK,
261 },
262 [UACPI_REGISTER_FIELD_RTC_STS] = {
263 .reg = UACPI_REGISTER_PM1_STS,
264 .offset = ACPI_PM1_STS_RTC_STS_IDX,
265 .mask = ACPI_PM1_STS_RTC_STS_MASK,
266 },
267 [UACPI_REGISTER_FIELD_HWR_WAK_STS] = {
268 .reg = UACPI_REGISTER_SLP_STS,
269 .offset = ACPI_SLP_STS_WAK_STS_IDX,
270 .mask = ACPI_SLP_STS_WAK_STS_MASK,
271 },
272 [UACPI_REGISTER_FIELD_WAK_STS] = {
273 .reg = UACPI_REGISTER_PM1_STS,
274 .offset = ACPI_PM1_STS_WAKE_STS_IDX,
275 .mask = ACPI_PM1_STS_WAKE_STS_MASK,
276 },
277 [UACPI_REGISTER_FIELD_PCIEX_WAKE_STS] = {
278 .reg = UACPI_REGISTER_PM1_STS,
279 .offset = ACPI_PM1_STS_PCIEXP_WAKE_STS_IDX,
280 .mask = ACPI_PM1_STS_PCIEXP_WAKE_STS_MASK,
281 },
282 [UACPI_REGISTER_FIELD_TMR_EN] = {
283 .reg = UACPI_REGISTER_PM1_EN,
284 .offset = ACPI_PM1_EN_TMR_EN_IDX,
285 .mask = ACPI_PM1_EN_TMR_EN_MASK,
286 },
287 [UACPI_REGISTER_FIELD_GBL_EN] = {
288 .reg = UACPI_REGISTER_PM1_EN,
289 .offset = ACPI_PM1_EN_GBL_EN_IDX,
290 .mask = ACPI_PM1_EN_GBL_EN_MASK,
291 },
292 [UACPI_REGISTER_FIELD_PWRBTN_EN] = {
293 .reg = UACPI_REGISTER_PM1_EN,
294 .offset = ACPI_PM1_EN_PWRBTN_EN_IDX,
295 .mask = ACPI_PM1_EN_PWRBTN_EN_MASK,
296 },
297 [UACPI_REGISTER_FIELD_SLPBTN_EN] = {
298 .reg = UACPI_REGISTER_PM1_EN,
299 .offset = ACPI_PM1_EN_SLPBTN_EN_IDX,
300 .mask = ACPI_PM1_EN_SLPBTN_EN_MASK,
301 },
302 [UACPI_REGISTER_FIELD_RTC_EN] = {
303 .reg = UACPI_REGISTER_PM1_EN,
304 .offset = ACPI_PM1_EN_RTC_EN_IDX,
305 .mask = ACPI_PM1_EN_RTC_EN_MASK,
306 },
307 [UACPI_REGISTER_FIELD_PCIEXP_WAKE_DIS] = {
308 .reg = UACPI_REGISTER_PM1_EN,
309 .offset = ACPI_PM1_EN_PCIEXP_WAKE_DIS_IDX,
310 .mask = ACPI_PM1_EN_PCIEXP_WAKE_DIS_MASK,
311 },
312 [UACPI_REGISTER_FIELD_SCI_EN] = {
313 .reg = UACPI_REGISTER_PM1_CNT,
314 .offset = ACPI_PM1_CNT_SCI_EN_IDX,
315 .mask = ACPI_PM1_CNT_SCI_EN_MASK,
316 },
317 [UACPI_REGISTER_FIELD_BM_RLD] = {
318 .reg = UACPI_REGISTER_PM1_CNT,
319 .offset = ACPI_PM1_CNT_BM_RLD_IDX,
320 .mask = ACPI_PM1_CNT_BM_RLD_MASK,
321 },
322 [UACPI_REGISTER_FIELD_GBL_RLS] = {
323 .reg = UACPI_REGISTER_PM1_CNT,
324 .offset = ACPI_PM1_CNT_GBL_RLS_IDX,
325 .mask = ACPI_PM1_CNT_GBL_RLS_MASK,
326 },
327 [UACPI_REGISTER_FIELD_SLP_TYP] = {
328 .reg = UACPI_REGISTER_PM1_CNT,
329 .offset = ACPI_PM1_CNT_SLP_TYP_IDX,
330 .mask = ACPI_PM1_CNT_SLP_TYP_MASK,
331 },
332 [UACPI_REGISTER_FIELD_SLP_EN] = {
333 .reg = UACPI_REGISTER_PM1_CNT,
334 .offset = ACPI_PM1_CNT_SLP_EN_IDX,
335 .mask = ACPI_PM1_CNT_SLP_EN_MASK,
336 },
337 [UACPI_REGISTER_FIELD_HWR_SLP_TYP] = {
338 .reg = UACPI_REGISTER_SLP_CNT,
339 .offset = ACPI_SLP_CNT_SLP_TYP_IDX,
340 .mask = ACPI_SLP_CNT_SLP_TYP_MASK,
341 },
342 [UACPI_REGISTER_FIELD_HWR_SLP_EN] = {
343 .reg = UACPI_REGISTER_SLP_CNT,
344 .offset = ACPI_SLP_CNT_SLP_EN_IDX,
345 .mask = ACPI_SLP_CNT_SLP_EN_MASK,
346 },
347 [UACPI_REGISTER_FIELD_ARB_DIS] = {
348 .reg = UACPI_REGISTER_PM2_CNT,
349 .offset = ACPI_PM2_CNT_ARB_DIS_IDX,
350 .mask = ACPI_PM2_CNT_ARB_DIS_MASK,
351 },
352};
353
354uacpi_status uacpi_read_register_field(
355 enum uacpi_register_field field_enum, uacpi_u64 *out_value
356)
357{
358 uacpi_status ret;
359 uacpi_u8 field_idx = field_enum;
360 struct register_field *field;
361 struct register_spec *reg;
362
363 if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX))
364 return UACPI_STATUS_INVALID_ARGUMENT;
365
366 field = &fields[field_idx];
367 reg = &registers[field->reg];
368
369 ret = do_read_register(reg, out_value);
370 if (uacpi_unlikely_error(ret))
371 return ret;
372
373 *out_value = (*out_value & field->mask) >> field->offset;
374 return UACPI_STATUS_OK;
375}
376
377uacpi_status uacpi_write_register_field(
378 enum uacpi_register_field field_enum, uacpi_u64 in_value
379)
380{
381 uacpi_status ret;
382 uacpi_u8 field_idx = field_enum;
383 struct register_field *field;
384 struct register_spec *reg;
385 uacpi_u64 data;
386
387 if (uacpi_unlikely(field_idx > UACPI_REGISTER_FIELD_MAX))
388 return UACPI_STATUS_INVALID_ARGUMENT;
389
390 field = &fields[field_idx];
391 reg = &registers[field->reg];
392
393 in_value = (in_value << field->offset) & field->mask;
394
395 if (reg->kind == REGISTER_ACCESS_KIND_WRITE_TO_CLEAR) {
396 if (in_value == 0)
397 return UACPI_STATUS_OK;
398
399 return do_write_register(reg, in_value);
400 }
401
402 ret = do_read_register(reg, out_value: &data);
403 if (uacpi_unlikely_error(ret))
404 return ret;
405
406 data &= ~field->mask;
407 data |= in_value;
408
409 return do_write_register(reg, in_value: data);
410}
411