1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Johannes Lundberg <johalun@FreeBSD.org> 5 * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include "opt_acpi.h" 33 34 #include <sys/types.h> 35 #include <sys/bus.h> 36 #include <sys/eventhandler.h> 37 #include <sys/kernel.h> 38 39 #include <contrib/dev/acpica/include/acpi.h> 40 #include <dev/acpica/acpivar.h> 41 42 #include <linux/notifier.h> 43 #include <linux/suspend.h> 44 45 #include <acpi/acpi_bus.h> 46 #include <acpi/video.h> 47 48 #define ACPI_AC_CLASS "ac_adapter" 49 50 ACPI_MODULE_NAME("linux_acpi") 51 52 enum { 53 LINUX_ACPI_ACAD, 54 LINUX_ACPI_VIDEO, 55 LINUX_ACPI_TAGS /* must be last */ 56 }; 57 _Static_assert(LINUX_ACPI_TAGS <= LINUX_NOTIFY_TAGS, 58 "Not enough space for tags in notifier_block structure"); 59 60 #ifdef DEV_ACPI 61 62 suspend_state_t pm_suspend_target_state = PM_SUSPEND_ON; 63 64 static uint32_t linux_acpi_target_sleep_state = ACPI_STATE_S0; 65 66 static eventhandler_tag resume_tag; 67 static eventhandler_tag suspend_tag; 68 69 ACPI_HANDLE 70 bsd_acpi_get_handle(device_t bsddev) 71 { 72 return (acpi_get_handle(bsddev)); 73 } 74 75 bool 76 acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev, uint64_t funcs) 77 { 78 79 if (funcs == 0) 80 return (false); 81 82 /* 83 * From ACPI 6.3 spec 9.1.1: 84 * Bit 0 indicates whether there is support for any functions other 85 * than function 0 for the specified UUID and Revision ID. If set to 86 * zero, no functions are supported (other than function zero) for the 87 * specified UUID and Revision ID. 88 */ 89 funcs |= 1 << 0; 90 91 return ((acpi_DSMQuery(handle, uuid, rev) & funcs) == funcs); 92 } 93 94 ACPI_OBJECT * 95 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev, 96 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type) 97 { 98 ACPI_BUFFER buf; 99 100 return (ACPI_SUCCESS(acpi_EvaluateDSMTyped(handle, uuid, rev, func, 101 argv4, &buf, type)) ? (ACPI_OBJECT *)buf.Pointer : NULL); 102 } 103 104 static void 105 linux_handle_power_suspend_event(void *arg __unused) 106 { 107 /* 108 * Only support S3 for now. 109 * acpi_sleep_event isn't always called so we use power_suspend_early 110 * instead which means we don't know what state we're switching to. 111 * TODO: Make acpi_sleep_event consistent 112 */ 113 linux_acpi_target_sleep_state = ACPI_STATE_S3; 114 } 115 116 static void 117 linux_handle_power_resume_event(void *arg __unused) 118 { 119 linux_acpi_target_sleep_state = ACPI_STATE_S0; 120 } 121 122 static void 123 linux_handle_acpi_acad_event(void *arg, int data) 124 { 125 struct notifier_block *nb = arg; 126 /* 127 * Event type information is lost ATM in FreeBSD ACPI event handler. 128 * Fortunately, drm-kmod do not distinct AC event types too, so we can 129 * use any type e.g. ACPI_NOTIFY_BUS_CHECK that suits notifier handler. 130 */ 131 struct acpi_bus_event abe = { 132 .device_class = ACPI_AC_CLASS, 133 .type = ACPI_NOTIFY_BUS_CHECK, 134 .data = data, 135 }; 136 137 nb->notifier_call(nb, 0, &abe); 138 } 139 140 static void 141 linux_handle_acpi_video_event(void *arg, int type) 142 { 143 struct notifier_block *nb = arg; 144 struct acpi_bus_event abe = { 145 .device_class = ACPI_VIDEO_CLASS, 146 .type = type, 147 .data = 0, 148 }; 149 150 nb->notifier_call(nb, 0, &abe); 151 } 152 153 int 154 register_acpi_notifier(struct notifier_block *nb) 155 { 156 nb->tags[LINUX_ACPI_ACAD] = EVENTHANDLER_REGISTER(acpi_acad_event, 157 linux_handle_acpi_acad_event, nb, EVENTHANDLER_PRI_FIRST); 158 nb->tags[LINUX_ACPI_VIDEO] = EVENTHANDLER_REGISTER(acpi_video_event, 159 linux_handle_acpi_video_event, nb, EVENTHANDLER_PRI_FIRST); 160 161 return (0); 162 } 163 164 int 165 unregister_acpi_notifier(struct notifier_block *nb) 166 { 167 EVENTHANDLER_DEREGISTER(acpi_acad_event, nb->tags[LINUX_ACPI_ACAD]); 168 EVENTHANDLER_DEREGISTER(acpi_video_event, nb->tags[LINUX_ACPI_VIDEO]); 169 170 return (0); 171 } 172 173 uint32_t 174 acpi_target_system_state(void) 175 { 176 return (linux_acpi_target_sleep_state); 177 } 178 179 static void 180 linux_register_acpi_event_handlers(void *arg __unused) 181 { 182 /* 183 * XXX johalun: acpi_{sleep,wakeup}_event can't be trusted, use 184 * power_{suspend_early,resume} 'acpiconf -s 3' or 'zzz' will not 185 * generate acpi_sleep_event... Lid open or wake on button generates 186 * acpi_wakeup_event on one of my Dell laptops but not the other 187 * (but it does power on)... is this a general thing? 188 */ 189 resume_tag = EVENTHANDLER_REGISTER(power_resume, 190 linux_handle_power_resume_event, NULL, EVENTHANDLER_PRI_FIRST); 191 suspend_tag = EVENTHANDLER_REGISTER(power_suspend_early, 192 linux_handle_power_suspend_event, NULL, EVENTHANDLER_PRI_FIRST); 193 } 194 195 static void 196 linux_deregister_acpi_event_handlers(void *arg __unused) 197 { 198 EVENTHANDLER_DEREGISTER(power_resume, resume_tag); 199 EVENTHANDLER_DEREGISTER(power_suspend_early, suspend_tag); 200 } 201 202 SYSINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 203 linux_register_acpi_event_handlers, NULL); 204 SYSUNINIT(linux_acpi_events, SI_SUB_DRIVERS, SI_ORDER_ANY, 205 linux_deregister_acpi_event_handlers, NULL); 206 207 #else /* !DEV_ACPI */ 208 209 ACPI_HANDLE 210 bsd_acpi_get_handle(device_t bsddev) 211 { 212 return (NULL); 213 } 214 215 bool 216 acpi_check_dsm(ACPI_HANDLE handle, const char *uuid, int rev, uint64_t funcs) 217 { 218 return (false); 219 } 220 221 ACPI_OBJECT * 222 acpi_evaluate_dsm_typed(ACPI_HANDLE handle, const char *uuid, int rev, 223 int func, ACPI_OBJECT *argv4, ACPI_OBJECT_TYPE type) 224 { 225 return (NULL); 226 } 227 228 int 229 register_acpi_notifier(struct notifier_block *nb) 230 { 231 return (0); 232 } 233 234 int 235 unregister_acpi_notifier(struct notifier_block *nb) 236 { 237 return (0); 238 } 239 240 uint32_t 241 acpi_target_system_state(void) 242 { 243 return (ACPI_STATE_S0); 244 } 245 246 #endif /* !DEV_ACPI */ 247