1*44ceda0aSAbdelkader Boudih /*
2*44ceda0aSAbdelkader Boudih * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
3*44ceda0aSAbdelkader Boudih *
4*44ceda0aSAbdelkader Boudih * SPDX-License-Identifier: BSD-2-Clause
5*44ceda0aSAbdelkader Boudih */
6*44ceda0aSAbdelkader Boudih
7*44ceda0aSAbdelkader Boudih /*
8*44ceda0aSAbdelkader Boudih * Apple platform ACPI: Wake-on-LAN and lid state.
9*44ceda0aSAbdelkader Boudih *
10*44ceda0aSAbdelkader Boudih * Walks the ACPI namespace on shutdown calling _PSW(1) on any device
11*44ceda0aSAbdelkader Boudih * with ACPI_FLAG_WAKE_ENABLED set, arming EC.EWOL for wake on magic
12*44ceda0aSAbdelkader Boudih * packet. Also follows acpi_lid(4) to blank and restore the panel
13*44ceda0aSAbdelkader Boudih * backlight.
14*44ceda0aSAbdelkader Boudih */
15*44ceda0aSAbdelkader Boudih
16*44ceda0aSAbdelkader Boudih #include <sys/cdefs.h>
17*44ceda0aSAbdelkader Boudih #include "acpi_apple.h"
18*44ceda0aSAbdelkader Boudih
19*44ceda0aSAbdelkader Boudih /*
20*44ceda0aSAbdelkader Boudih * APP9876 and APP000B attach concurrently as ACPI_APPLE_PLATFORM on T2
21*44ceda0aSAbdelkader Boudih * Macs with a discrete GPU. Refcount the shutdown and lid hooks here so
22*44ceda0aSAbdelkader Boudih * the second instance's attach/detach doesn't reinstall or rip out the
23*44ceda0aSAbdelkader Boudih * first instance's hooks.
24*44ceda0aSAbdelkader Boudih */
25*44ceda0aSAbdelkader Boudih static struct mtx acpi_apple_platform_mtx;
26*44ceda0aSAbdelkader Boudih MTX_SYSINIT(acpi_apple_platform_mtx_init, &acpi_apple_platform_mtx,
27*44ceda0aSAbdelkader Boudih "acpi_apple_platform", MTX_DEF);
28*44ceda0aSAbdelkader Boudih static int acpi_apple_platform_refs;
29*44ceda0aSAbdelkader Boudih static eventhandler_tag acpi_apple_shutdown_tag;
30*44ceda0aSAbdelkader Boudih static eventhandler_tag acpi_apple_lid_tag;
31*44ceda0aSAbdelkader Boudih
32*44ceda0aSAbdelkader Boudih static ACPI_STATUS
acpi_apple_psw_cb(ACPI_HANDLE handle,UINT32 level __unused,void * context,void ** retval __unused)33*44ceda0aSAbdelkader Boudih acpi_apple_psw_cb(ACPI_HANDLE handle, UINT32 level __unused, void *context,
34*44ceda0aSAbdelkader Boudih void **retval __unused)
35*44ceda0aSAbdelkader Boudih {
36*44ceda0aSAbdelkader Boudih ACPI_HANDLE psw_handle;
37*44ceda0aSAbdelkader Boudih device_t dev;
38*44ceda0aSAbdelkader Boudih int enable;
39*44ceda0aSAbdelkader Boudih
40*44ceda0aSAbdelkader Boudih enable = (int)(uintptr_t)context;
41*44ceda0aSAbdelkader Boudih
42*44ceda0aSAbdelkader Boudih if (ACPI_FAILURE(AcpiGetHandle(handle, "_PSW", &psw_handle)))
43*44ceda0aSAbdelkader Boudih return (AE_OK);
44*44ceda0aSAbdelkader Boudih
45*44ceda0aSAbdelkader Boudih dev = acpi_get_device(handle);
46*44ceda0aSAbdelkader Boudih if (dev == NULL)
47*44ceda0aSAbdelkader Boudih return (AE_OK);
48*44ceda0aSAbdelkader Boudih
49*44ceda0aSAbdelkader Boudih if (enable && (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) == 0)
50*44ceda0aSAbdelkader Boudih return (AE_OK);
51*44ceda0aSAbdelkader Boudih
52*44ceda0aSAbdelkader Boudih (void)acpi_SetInteger(handle, "_PSW", enable);
53*44ceda0aSAbdelkader Boudih if (bootverbose)
54*44ceda0aSAbdelkader Boudih device_printf(dev, "acpi_apple: _PSW(%d) on %s\n",
55*44ceda0aSAbdelkader Boudih enable, acpi_name(handle));
56*44ceda0aSAbdelkader Boudih
57*44ceda0aSAbdelkader Boudih return (AE_OK);
58*44ceda0aSAbdelkader Boudih }
59*44ceda0aSAbdelkader Boudih
60*44ceda0aSAbdelkader Boudih static void
acpi_apple_arm_wol(void * arg __unused,int howto __unused)61*44ceda0aSAbdelkader Boudih acpi_apple_arm_wol(void *arg __unused, int howto __unused)
62*44ceda0aSAbdelkader Boudih {
63*44ceda0aSAbdelkader Boudih AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
64*44ceda0aSAbdelkader Boudih ACPI_UINT32_MAX, acpi_apple_psw_cb, NULL,
65*44ceda0aSAbdelkader Boudih (void *)(uintptr_t)1, NULL);
66*44ceda0aSAbdelkader Boudih }
67*44ceda0aSAbdelkader Boudih
68*44ceda0aSAbdelkader Boudih /*
69*44ceda0aSAbdelkader Boudih * The GMUX handshake polls for milliseconds, so keep it out of the
70*44ceda0aSAbdelkader Boudih * acpi_lid(4) notify path, which runs with ACPI_SERIAL(lid) held.
71*44ceda0aSAbdelkader Boudih */
72*44ceda0aSAbdelkader Boudih static void
acpi_apple_backlight_task(void * context)73*44ceda0aSAbdelkader Boudih acpi_apple_backlight_task(void *context)
74*44ceda0aSAbdelkader Boudih {
75*44ceda0aSAbdelkader Boudih if (context != NULL)
76*44ceda0aSAbdelkader Boudih acpi_apple_backlight_restore();
77*44ceda0aSAbdelkader Boudih else
78*44ceda0aSAbdelkader Boudih acpi_apple_backlight_off();
79*44ceda0aSAbdelkader Boudih }
80*44ceda0aSAbdelkader Boudih
81*44ceda0aSAbdelkader Boudih static void
acpi_apple_lid_event(void * arg __unused,int lid_open)82*44ceda0aSAbdelkader Boudih acpi_apple_lid_event(void *arg __unused, int lid_open)
83*44ceda0aSAbdelkader Boudih {
84*44ceda0aSAbdelkader Boudih AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_apple_backlight_task,
85*44ceda0aSAbdelkader Boudih (void *)(uintptr_t)lid_open);
86*44ceda0aSAbdelkader Boudih }
87*44ceda0aSAbdelkader Boudih
88*44ceda0aSAbdelkader Boudih int
acpi_apple_platform_attach(struct acpi_apple_softc * sc __unused)89*44ceda0aSAbdelkader Boudih acpi_apple_platform_attach(struct acpi_apple_softc *sc __unused)
90*44ceda0aSAbdelkader Boudih {
91*44ceda0aSAbdelkader Boudih mtx_lock(&acpi_apple_platform_mtx);
92*44ceda0aSAbdelkader Boudih if (acpi_apple_platform_refs++ == 0) {
93*44ceda0aSAbdelkader Boudih acpi_apple_shutdown_tag = EVENTHANDLER_REGISTER(
94*44ceda0aSAbdelkader Boudih shutdown_final, acpi_apple_arm_wol, NULL,
95*44ceda0aSAbdelkader Boudih SHUTDOWN_PRI_LAST);
96*44ceda0aSAbdelkader Boudih acpi_apple_lid_tag = EVENTHANDLER_REGISTER(acpi_lid_event,
97*44ceda0aSAbdelkader Boudih acpi_apple_lid_event, NULL, EVENTHANDLER_PRI_ANY);
98*44ceda0aSAbdelkader Boudih }
99*44ceda0aSAbdelkader Boudih mtx_unlock(&acpi_apple_platform_mtx);
100*44ceda0aSAbdelkader Boudih
101*44ceda0aSAbdelkader Boudih return (0);
102*44ceda0aSAbdelkader Boudih }
103*44ceda0aSAbdelkader Boudih
104*44ceda0aSAbdelkader Boudih void
acpi_apple_platform_detach(struct acpi_apple_softc * sc __unused)105*44ceda0aSAbdelkader Boudih acpi_apple_platform_detach(struct acpi_apple_softc *sc __unused)
106*44ceda0aSAbdelkader Boudih {
107*44ceda0aSAbdelkader Boudih mtx_lock(&acpi_apple_platform_mtx);
108*44ceda0aSAbdelkader Boudih KASSERT(acpi_apple_platform_refs > 0,
109*44ceda0aSAbdelkader Boudih ("acpi_apple: platform refcount underflow"));
110*44ceda0aSAbdelkader Boudih if (acpi_apple_platform_refs > 0 && --acpi_apple_platform_refs == 0) {
111*44ceda0aSAbdelkader Boudih if (acpi_apple_lid_tag != NULL) {
112*44ceda0aSAbdelkader Boudih EVENTHANDLER_DEREGISTER(acpi_lid_event,
113*44ceda0aSAbdelkader Boudih acpi_apple_lid_tag);
114*44ceda0aSAbdelkader Boudih acpi_apple_lid_tag = NULL;
115*44ceda0aSAbdelkader Boudih }
116*44ceda0aSAbdelkader Boudih if (acpi_apple_shutdown_tag != NULL) {
117*44ceda0aSAbdelkader Boudih EVENTHANDLER_DEREGISTER(shutdown_final,
118*44ceda0aSAbdelkader Boudih acpi_apple_shutdown_tag);
119*44ceda0aSAbdelkader Boudih acpi_apple_shutdown_tag = NULL;
120*44ceda0aSAbdelkader Boudih }
121*44ceda0aSAbdelkader Boudih }
122*44ceda0aSAbdelkader Boudih mtx_unlock(&acpi_apple_platform_mtx);
123*44ceda0aSAbdelkader Boudih }
124