1 | #include <uacpi/sleep.h> |
2 | #include <uacpi/internal/context.h> |
3 | #include <uacpi/internal/log.h> |
4 | #include <uacpi/internal/io.h> |
5 | #include <uacpi/internal/registers.h> |
6 | #include <uacpi/internal/event.h> |
7 | #include <uacpi/platform/arch_helpers.h> |
8 | |
9 | #ifndef UACPI_REDUCED_HARDWARE |
10 | #define CALL_SLEEP_FN(name, state) \ |
11 | (uacpi_is_hardware_reduced() ? \ |
12 | name##_hw_reduced(state) : name##_hw_full(state)) |
13 | #else |
14 | #define CALL_SLEEP_FN(name, state) name##_hw_reduced(state); |
15 | #endif |
16 | |
17 | static uacpi_status eval_wak(uacpi_u8 state); |
18 | static uacpi_status eval_sst(uacpi_u8 value); |
19 | |
20 | #ifndef UACPI_REDUCED_HARDWARE |
21 | uacpi_status uacpi_set_waking_vector( |
22 | uacpi_phys_addr addr32, uacpi_phys_addr addr64 |
23 | ) |
24 | { |
25 | struct acpi_facs *facs = g_uacpi_rt_ctx.facs; |
26 | |
27 | if (facs == UACPI_NULL) |
28 | return UACPI_STATUS_OK; |
29 | |
30 | facs->firmware_waking_vector = addr32; |
31 | |
32 | // The 64-bit wake vector doesn't exist, we're done |
33 | if (facs->length < 32) |
34 | return UACPI_STATUS_OK; |
35 | |
36 | // Only allow 64-bit wake vector on 1.0 and above FACS |
37 | if (facs->version >= 1) |
38 | facs->x_firmware_waking_vector = addr64; |
39 | else |
40 | facs->x_firmware_waking_vector = 0; |
41 | |
42 | return UACPI_STATUS_OK; |
43 | } |
44 | |
45 | static uacpi_status enter_sleep_state_hw_full(uacpi_u8 state) |
46 | { |
47 | uacpi_status ret; |
48 | uacpi_u64 wake_status, pm1a, pm1b; |
49 | |
50 | ret = uacpi_write_register_field( |
51 | UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR |
52 | ); |
53 | if (uacpi_unlikely_error(ret)) |
54 | return ret; |
55 | |
56 | ret = uacpi_disable_all_gpes(); |
57 | if (uacpi_unlikely_error(ret)) |
58 | return ret; |
59 | |
60 | ret = uacpi_clear_all_events(); |
61 | if (uacpi_unlikely_error(ret)) |
62 | return ret; |
63 | |
64 | ret = uacpi_enable_all_wake_gpes(); |
65 | if (uacpi_unlikely_error(ret)) |
66 | return ret; |
67 | |
68 | ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a); |
69 | if (uacpi_unlikely_error(ret)) |
70 | return ret; |
71 | |
72 | pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK)); |
73 | pm1b = pm1a; |
74 | |
75 | pm1a |= g_uacpi_rt_ctx.last_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX; |
76 | pm1b |= g_uacpi_rt_ctx.last_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX; |
77 | |
78 | /* |
79 | * Just like ACPICA, split writing SLP_TYP and SLP_EN to work around |
80 | * buggy firmware that can't handle both written at the same time. |
81 | */ |
82 | ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b); |
83 | if (uacpi_unlikely_error(ret)) |
84 | return ret; |
85 | |
86 | pm1a |= ACPI_PM1_CNT_SLP_EN_MASK; |
87 | pm1b |= ACPI_PM1_CNT_SLP_EN_MASK; |
88 | |
89 | if (state < UACPI_SLEEP_STATE_S4) |
90 | UACPI_ARCH_FLUSH_CPU_CACHE(); |
91 | |
92 | ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b); |
93 | if (uacpi_unlikely_error(ret)) |
94 | return ret; |
95 | |
96 | if (state > UACPI_SLEEP_STATE_S3) { |
97 | /* |
98 | * We're still here, this is a bug or very slow firmware. |
99 | * Just try spinning for a bit. |
100 | */ |
101 | uacpi_u64 stalled_time = 0; |
102 | |
103 | // 10 seconds max |
104 | while (stalled_time < (10 * 1000 * 1000)) { |
105 | uacpi_kernel_stall(usec: 100); |
106 | stalled_time += 100; |
107 | } |
108 | |
109 | // Try one more time |
110 | ret = uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b); |
111 | if (uacpi_unlikely_error(ret)) |
112 | return ret; |
113 | |
114 | // Nothing we can do here, give up |
115 | return UACPI_STATUS_HARDWARE_TIMEOUT; |
116 | } |
117 | |
118 | do { |
119 | ret = uacpi_read_register_field( |
120 | UACPI_REGISTER_FIELD_WAK_STS, &wake_status |
121 | ); |
122 | if (uacpi_unlikely_error(ret)) |
123 | return ret; |
124 | } while (wake_status != 1); |
125 | |
126 | return UACPI_STATUS_OK; |
127 | } |
128 | |
129 | static uacpi_status prepare_for_wake_from_sleep_state_hw_full(uacpi_u8 state) |
130 | { |
131 | uacpi_status ret; |
132 | uacpi_u64 pm1a, pm1b; |
133 | UACPI_UNUSED(state); |
134 | |
135 | /* |
136 | * Some hardware apparently relies on S0 values being written to the PM1 |
137 | * control register on wake, so do this here. |
138 | */ |
139 | |
140 | if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID) |
141 | goto out; |
142 | |
143 | ret = uacpi_read_register(UACPI_REGISTER_PM1_CNT, &pm1a); |
144 | if (uacpi_unlikely_error(ret)) |
145 | goto out; |
146 | |
147 | pm1a &= ~((uacpi_u64)(ACPI_PM1_CNT_SLP_TYP_MASK | ACPI_PM1_CNT_SLP_EN_MASK)); |
148 | pm1b = pm1a; |
149 | |
150 | pm1a |= g_uacpi_rt_ctx.s0_sleep_typ_a << ACPI_PM1_CNT_SLP_TYP_IDX; |
151 | pm1b |= g_uacpi_rt_ctx.s0_sleep_typ_b << ACPI_PM1_CNT_SLP_TYP_IDX; |
152 | |
153 | uacpi_write_registers(UACPI_REGISTER_PM1_CNT, pm1a, pm1b); |
154 | out: |
155 | // Errors ignored intentionally, we don't want to abort because of this |
156 | return UACPI_STATUS_OK; |
157 | } |
158 | |
159 | static uacpi_status wake_from_sleep_state_hw_full(uacpi_u8 state) |
160 | { |
161 | uacpi_status ret; |
162 | g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID; |
163 | g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID; |
164 | |
165 | // Set the status to 2 (waking) while we execute the wake method. |
166 | eval_sst(value: 2); |
167 | |
168 | ret = uacpi_disable_all_gpes(); |
169 | if (uacpi_unlikely_error(ret)) |
170 | return ret; |
171 | |
172 | ret = uacpi_enable_all_runtime_gpes(); |
173 | if (uacpi_unlikely_error(ret)) |
174 | return ret; |
175 | |
176 | eval_wak(state); |
177 | |
178 | // Apparently some BIOSes expect us to clear this, so do it |
179 | uacpi_write_register_field( |
180 | UACPI_REGISTER_FIELD_WAK_STS, ACPI_PM1_STS_CLEAR |
181 | ); |
182 | |
183 | // Now that we're awake set the status to 1 (running) |
184 | eval_sst(value: 1); |
185 | |
186 | return UACPI_STATUS_OK; |
187 | } |
188 | #endif |
189 | |
190 | static uacpi_status get_slp_type_for_state( |
191 | uacpi_u8 state, uacpi_u8 *a, uacpi_u8 *b |
192 | ) |
193 | { |
194 | uacpi_char path[] = "_S0" ; |
195 | uacpi_status ret; |
196 | uacpi_object *obj0, *obj1, *ret_obj = UACPI_NULL; |
197 | |
198 | path[2] += state; |
199 | |
200 | ret = uacpi_eval_typed( |
201 | parent: uacpi_namespace_root(), path, UACPI_NULL, |
202 | UACPI_OBJECT_PACKAGE_BIT, ret: &ret_obj |
203 | ); |
204 | if (ret != UACPI_STATUS_OK) { |
205 | if (uacpi_unlikely(ret != UACPI_STATUS_NOT_FOUND)) { |
206 | uacpi_warn("error while evaluating %s: %s\n" , path, |
207 | uacpi_status_to_string(ret)); |
208 | } else { |
209 | uacpi_trace("sleep state %d is not supported as %s was not found\n" , |
210 | state, path); |
211 | } |
212 | goto out; |
213 | } |
214 | |
215 | switch (ret_obj->package->count) { |
216 | case 0: |
217 | uacpi_error("empty package while evaluating %s!\n" , path); |
218 | ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE; |
219 | goto out; |
220 | |
221 | case 1: |
222 | obj0 = ret_obj->package->objects[0]; |
223 | if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER)) { |
224 | uacpi_error( |
225 | "invalid object type at pkg[0] => %s when evaluating %s\n" , |
226 | uacpi_object_type_to_string(obj0->type), path |
227 | ); |
228 | goto out; |
229 | } |
230 | |
231 | *a = obj0->integer; |
232 | *b = obj0->integer >> 8; |
233 | break; |
234 | |
235 | default: |
236 | obj0 = ret_obj->package->objects[0]; |
237 | obj1 = ret_obj->package->objects[1]; |
238 | |
239 | if (uacpi_unlikely(obj0->type != UACPI_OBJECT_INTEGER || |
240 | obj1->type != UACPI_OBJECT_INTEGER)) { |
241 | uacpi_error( |
242 | "invalid object type when evaluating %s: " |
243 | "pkg[0] => %s, pkg[1] => %s\n" , path, |
244 | uacpi_object_type_to_string(obj0->type), |
245 | uacpi_object_type_to_string(obj1->type) |
246 | ); |
247 | ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE; |
248 | goto out; |
249 | } |
250 | |
251 | *a = obj0->integer; |
252 | *b = obj1->integer; |
253 | break; |
254 | } |
255 | |
256 | out: |
257 | if (ret != UACPI_STATUS_OK) { |
258 | *a = UACPI_SLEEP_TYP_INVALID; |
259 | *b = UACPI_SLEEP_TYP_INVALID; |
260 | } |
261 | |
262 | uacpi_object_unref(obj: ret_obj); |
263 | return ret; |
264 | } |
265 | |
266 | static uacpi_status eval_sleep_helper( |
267 | uacpi_namespace_node *parent, const uacpi_char *path, uacpi_u8 value |
268 | ) |
269 | { |
270 | uacpi_object *arg; |
271 | uacpi_args args; |
272 | uacpi_status ret; |
273 | |
274 | arg = uacpi_create_object(type: UACPI_OBJECT_INTEGER); |
275 | if (uacpi_unlikely(arg == UACPI_NULL)) |
276 | return UACPI_STATUS_OUT_OF_MEMORY; |
277 | |
278 | arg->integer = value; |
279 | args.objects = &arg; |
280 | args.count = 1; |
281 | |
282 | ret = uacpi_eval(parent, path, args: &args, UACPI_NULL); |
283 | switch (ret) { |
284 | case UACPI_STATUS_OK: |
285 | break; |
286 | case UACPI_STATUS_NOT_FOUND: |
287 | ret = UACPI_STATUS_OK; |
288 | break; |
289 | default: |
290 | uacpi_error("error while evaluating %s: %s\n" , |
291 | path, uacpi_status_to_string(ret)); |
292 | break; |
293 | } |
294 | |
295 | uacpi_object_unref(obj: arg); |
296 | return ret; |
297 | } |
298 | |
299 | static uacpi_status eval_pts(uacpi_u8 state) |
300 | { |
301 | return eval_sleep_helper(parent: uacpi_namespace_root(), path: "_PTS" , value: state); |
302 | } |
303 | |
304 | static uacpi_status eval_wak(uacpi_u8 state) |
305 | { |
306 | return eval_sleep_helper(parent: uacpi_namespace_root(), path: "_WAK" , value: state); |
307 | } |
308 | |
309 | static uacpi_status eval_sst(uacpi_u8 value) |
310 | { |
311 | return eval_sleep_helper( |
312 | parent: uacpi_namespace_get_predefined(UACPI_PREDEFINED_NAMESPACE_SI), |
313 | path: "_SST" , value |
314 | ); |
315 | } |
316 | |
317 | static uacpi_status eval_sst_for_state(enum uacpi_sleep_state state) |
318 | { |
319 | uacpi_u8 arg; |
320 | |
321 | /* |
322 | * This optional object is a control method that OSPM invokes to set the |
323 | * system status indicator as desired. |
324 | * Arguments:(1) |
325 | * Arg0 - An Integer containing the system status indicator identifier: |
326 | * 0 - No system state indication. Indicator off |
327 | * 1 - Working |
328 | * 2 - Waking |
329 | * 3 - Sleeping. Used to indicate system state S1, S2, or S3 |
330 | * 4 - Sleeping with context saved to non-volatile storage |
331 | */ |
332 | switch (state) { |
333 | case UACPI_SLEEP_STATE_S0: |
334 | arg = 1; |
335 | break; |
336 | case UACPI_SLEEP_STATE_S1: |
337 | case UACPI_SLEEP_STATE_S2: |
338 | case UACPI_SLEEP_STATE_S3: |
339 | arg = 3; |
340 | break; |
341 | case UACPI_SLEEP_STATE_S4: |
342 | arg = 4; |
343 | break; |
344 | case UACPI_SLEEP_STATE_S5: |
345 | arg = 0; |
346 | break; |
347 | default: |
348 | return UACPI_STATUS_INVALID_ARGUMENT; |
349 | } |
350 | |
351 | return eval_sst(value: arg); |
352 | } |
353 | |
354 | uacpi_status uacpi_prepare_for_sleep_state(enum uacpi_sleep_state state_enum) |
355 | { |
356 | uacpi_u8 state = state_enum; |
357 | uacpi_status ret; |
358 | |
359 | UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED); |
360 | |
361 | if (uacpi_unlikely(state > UACPI_SLEEP_STATE_S5)) |
362 | return UACPI_STATUS_INVALID_ARGUMENT; |
363 | |
364 | ret = get_slp_type_for_state( |
365 | state, |
366 | a: &g_uacpi_rt_ctx.last_sleep_typ_a, |
367 | b: &g_uacpi_rt_ctx.last_sleep_typ_b |
368 | ); |
369 | if (ret != UACPI_STATUS_OK) |
370 | return ret; |
371 | |
372 | ret = get_slp_type_for_state( |
373 | state: 0, |
374 | a: &g_uacpi_rt_ctx.s0_sleep_typ_a, |
375 | b: &g_uacpi_rt_ctx.s0_sleep_typ_b |
376 | ); |
377 | |
378 | ret = eval_pts(state); |
379 | if (uacpi_unlikely_error(ret)) |
380 | return ret; |
381 | |
382 | eval_sst_for_state(state); |
383 | return UACPI_STATUS_OK; |
384 | } |
385 | |
386 | static uacpi_u8 make_hw_reduced_sleep_control(uacpi_u8 slp_typ) |
387 | { |
388 | uacpi_u8 value; |
389 | |
390 | value = (slp_typ << ACPI_SLP_CNT_SLP_TYP_IDX); |
391 | value &= ACPI_SLP_CNT_SLP_TYP_MASK; |
392 | value |= ACPI_SLP_CNT_SLP_EN_MASK; |
393 | |
394 | return value; |
395 | } |
396 | |
397 | static uacpi_status enter_sleep_state_hw_reduced(uacpi_u8 state) |
398 | { |
399 | uacpi_status ret; |
400 | uacpi_u8 sleep_control; |
401 | uacpi_u64 wake_status; |
402 | struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt; |
403 | |
404 | if (!fadt->sleep_control_reg.address || !fadt->sleep_status_reg.address) |
405 | return UACPI_STATUS_NOT_FOUND; |
406 | |
407 | ret = uacpi_write_register_field( |
408 | UACPI_REGISTER_FIELD_HWR_WAK_STS, |
409 | ACPI_SLP_STS_CLEAR |
410 | ); |
411 | if (uacpi_unlikely_error(ret)) |
412 | return ret; |
413 | |
414 | sleep_control = make_hw_reduced_sleep_control( |
415 | slp_typ: g_uacpi_rt_ctx.last_sleep_typ_a |
416 | ); |
417 | |
418 | if (state < UACPI_SLEEP_STATE_S4) |
419 | UACPI_ARCH_FLUSH_CPU_CACHE(); |
420 | |
421 | /* |
422 | * To put the system into a sleep state, software will write the HW-reduced |
423 | * Sleep Type value (obtained from the \_Sx object in the DSDT) and the |
424 | * SLP_EN bit to the sleep control register. |
425 | */ |
426 | ret = uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control); |
427 | if (uacpi_unlikely_error(ret)) |
428 | return ret; |
429 | |
430 | /* |
431 | * The OSPM then polls the WAK_STS bit of the SLEEP_STATUS_REG waiting for |
432 | * it to be one (1), indicating that the system has been transitioned |
433 | * back to the Working state. |
434 | */ |
435 | do { |
436 | ret = uacpi_read_register_field( |
437 | UACPI_REGISTER_FIELD_HWR_WAK_STS, &wake_status |
438 | ); |
439 | if (uacpi_unlikely_error(ret)) |
440 | return ret; |
441 | } while (wake_status != 1); |
442 | |
443 | return UACPI_STATUS_OK; |
444 | } |
445 | |
446 | static uacpi_status prepare_for_wake_from_sleep_state_hw_reduced(uacpi_u8 state) |
447 | { |
448 | uacpi_u8 sleep_control; |
449 | UACPI_UNUSED(state); |
450 | |
451 | if (g_uacpi_rt_ctx.s0_sleep_typ_a == UACPI_SLEEP_TYP_INVALID) |
452 | goto out; |
453 | |
454 | sleep_control = make_hw_reduced_sleep_control( |
455 | slp_typ: g_uacpi_rt_ctx.s0_sleep_typ_a |
456 | ); |
457 | uacpi_write_register(UACPI_REGISTER_SLP_CNT, sleep_control); |
458 | |
459 | out: |
460 | return UACPI_STATUS_OK; |
461 | } |
462 | |
463 | static uacpi_status wake_from_sleep_state_hw_reduced(uacpi_u8 state) |
464 | { |
465 | g_uacpi_rt_ctx.last_sleep_typ_a = UACPI_SLEEP_TYP_INVALID; |
466 | g_uacpi_rt_ctx.last_sleep_typ_b = UACPI_SLEEP_TYP_INVALID; |
467 | |
468 | // Set the status to 2 (waking) while we execute the wake method. |
469 | eval_sst(value: 2); |
470 | |
471 | eval_wak(state); |
472 | |
473 | // Apparently some BIOSes expect us to clear this, so do it |
474 | uacpi_write_register_field( |
475 | UACPI_REGISTER_FIELD_HWR_WAK_STS, ACPI_SLP_STS_CLEAR |
476 | ); |
477 | |
478 | // Now that we're awake set the status to 1 (running) |
479 | eval_sst(value: 1); |
480 | |
481 | return UACPI_STATUS_OK; |
482 | } |
483 | |
484 | uacpi_status uacpi_enter_sleep_state(enum uacpi_sleep_state state_enum) |
485 | { |
486 | uacpi_u8 state = state_enum; |
487 | |
488 | UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED); |
489 | |
490 | if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX)) |
491 | return UACPI_STATUS_INVALID_ARGUMENT; |
492 | |
493 | if (uacpi_unlikely(g_uacpi_rt_ctx.last_sleep_typ_a > ACPI_SLP_TYP_MAX || |
494 | g_uacpi_rt_ctx.last_sleep_typ_b > ACPI_SLP_TYP_MAX)) { |
495 | uacpi_error("invalid SLP_TYP values: 0x%02X:0x%02X\n" , |
496 | g_uacpi_rt_ctx.last_sleep_typ_a, |
497 | g_uacpi_rt_ctx.last_sleep_typ_b); |
498 | return UACPI_STATUS_AML_BAD_ENCODING; |
499 | } |
500 | |
501 | return CALL_SLEEP_FN(enter_sleep_state, state); |
502 | } |
503 | |
504 | uacpi_status uacpi_prepare_for_wake_from_sleep_state( |
505 | uacpi_sleep_state state_enum |
506 | ) |
507 | { |
508 | uacpi_u8 state = state_enum; |
509 | |
510 | UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED); |
511 | |
512 | if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX)) |
513 | return UACPI_STATUS_INVALID_ARGUMENT; |
514 | |
515 | return CALL_SLEEP_FN(prepare_for_wake_from_sleep_state, state); |
516 | } |
517 | |
518 | uacpi_status uacpi_wake_from_sleep_state( |
519 | uacpi_sleep_state state_enum |
520 | ) |
521 | { |
522 | uacpi_u8 state = state_enum; |
523 | |
524 | UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_INITIALIZED); |
525 | |
526 | if (uacpi_unlikely(state > UACPI_SLEEP_STATE_MAX)) |
527 | return UACPI_STATUS_INVALID_ARGUMENT; |
528 | |
529 | return CALL_SLEEP_FN(wake_from_sleep_state, state); |
530 | } |
531 | |
532 | uacpi_status uacpi_reboot(void) |
533 | { |
534 | uacpi_status ret; |
535 | struct acpi_fadt *fadt = &g_uacpi_rt_ctx.fadt; |
536 | struct acpi_gas *reset_reg = &fadt->reset_reg; |
537 | |
538 | /* |
539 | * Allow restarting earlier than namespace load so that the kernel can |
540 | * use this in case of some initialization error. |
541 | */ |
542 | UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_TABLES_LOADED); |
543 | |
544 | if (!(fadt->flags & ACPI_RESET_REG_SUP) || !reset_reg->address) |
545 | return UACPI_STATUS_NOT_FOUND; |
546 | |
547 | switch (reset_reg->address_space_id) { |
548 | case UACPI_ADDRESS_SPACE_SYSTEM_IO: |
549 | /* |
550 | * For SystemIO we don't do any checking, and we ignore bit width |
551 | * because that's what NT does. |
552 | */ |
553 | ret = uacpi_kernel_raw_io_write( |
554 | address: reset_reg->address, byte_width: 1, in_value: fadt->reset_value |
555 | ); |
556 | break; |
557 | case UACPI_ADDRESS_SPACE_SYSTEM_MEMORY: |
558 | ret = uacpi_write_register(UACPI_REGISTER_RESET, fadt->reset_value); |
559 | break; |
560 | case UACPI_ADDRESS_SPACE_PCI_CONFIG: { |
561 | // Bus is assumed to be 0 here |
562 | uacpi_pci_address address = { |
563 | .segment = 0, |
564 | .bus = 0, |
565 | .device = (reset_reg->address >> 32) & 0xFF, |
566 | .function = (reset_reg->address >> 16) & 0xFF, |
567 | }; |
568 | |
569 | ret = uacpi_kernel_pci_write( |
570 | address: &address, offset: reset_reg->address & 0xFFFF, byte_width: 1, value: fadt->reset_value |
571 | ); |
572 | break; |
573 | } |
574 | default: |
575 | uacpi_warn( |
576 | "unable to perform a reset: unsupported address space '%s' (%d)\n" , |
577 | uacpi_address_space_to_string(reset_reg->address_space_id), |
578 | reset_reg->address_space_id |
579 | ); |
580 | ret = UACPI_STATUS_UNIMPLEMENTED; |
581 | } |
582 | |
583 | if (ret == UACPI_STATUS_OK) { |
584 | /* |
585 | * This should've worked but we're still here. |
586 | * Spin for a bit then give up. |
587 | */ |
588 | uacpi_u64 stalled_time = 0; |
589 | |
590 | while (stalled_time < (1000 * 1000)) { |
591 | uacpi_kernel_stall(usec: 100); |
592 | stalled_time += 100; |
593 | } |
594 | |
595 | uacpi_error("reset timeout\n" ); |
596 | return UACPI_STATUS_HARDWARE_TIMEOUT; |
597 | } |
598 | |
599 | return ret; |
600 | } |
601 | |