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 | |
7 | enum register_kind { |
8 | REGISTER_KIND_GAS, |
9 | REGISTER_KIND_IO, |
10 | }; |
11 | |
12 | enum register_access_kind { |
13 | REGISTER_ACCESS_KIND_PRESERVE, |
14 | REGISTER_ACCESS_KIND_WRITE_TO_CLEAR, |
15 | REGISTER_ACCESS_KIND_NORMAL, |
16 | }; |
17 | |
18 | struct 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 | |
27 | static 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 | |
87 | static struct register_spec *get_reg(uacpi_u8 idx) |
88 | { |
89 | if (idx > UACPI_REGISTER_MAX) |
90 | return UACPI_NULL; |
91 | |
92 | return ®isters[idx]; |
93 | } |
94 | |
95 | static 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 | |
112 | static 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 | |
129 | static 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 | |
153 | uacpi_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 | |
166 | static 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 | |
196 | uacpi_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 | |
209 | uacpi_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 | |
230 | struct register_field { |
231 | uacpi_u8 reg; |
232 | uacpi_u8 offset; |
233 | uacpi_u16 mask; |
234 | }; |
235 | |
236 | static 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 | |
354 | uacpi_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 = ®isters[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 | |
377 | uacpi_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 = ®isters[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 | |