1 /****************************************************************************** 2 * 3 * Module Name: evgpeutil - GPE utilities 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2014, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include "acpi.h" 45 #include "accommon.h" 46 #include "acevents.h" 47 48 #define _COMPONENT ACPI_EVENTS 49 ACPI_MODULE_NAME ("evgpeutil") 50 51 52 #if (!ACPI_REDUCED_HARDWARE) /* Entire module */ 53 /******************************************************************************* 54 * 55 * FUNCTION: AcpiEvWalkGpeList 56 * 57 * PARAMETERS: GpeWalkCallback - Routine called for each GPE block 58 * Context - Value passed to callback 59 * 60 * RETURN: Status 61 * 62 * DESCRIPTION: Walk the GPE lists. 63 * 64 ******************************************************************************/ 65 66 ACPI_STATUS 67 AcpiEvWalkGpeList ( 68 ACPI_GPE_CALLBACK GpeWalkCallback, 69 void *Context) 70 { 71 ACPI_GPE_BLOCK_INFO *GpeBlock; 72 ACPI_GPE_XRUPT_INFO *GpeXruptInfo; 73 ACPI_STATUS Status = AE_OK; 74 ACPI_CPU_FLAGS Flags; 75 76 77 ACPI_FUNCTION_TRACE (EvWalkGpeList); 78 79 80 Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock); 81 82 /* Walk the interrupt level descriptor list */ 83 84 GpeXruptInfo = AcpiGbl_GpeXruptListHead; 85 while (GpeXruptInfo) 86 { 87 /* Walk all Gpe Blocks attached to this interrupt level */ 88 89 GpeBlock = GpeXruptInfo->GpeBlockListHead; 90 while (GpeBlock) 91 { 92 /* One callback per GPE block */ 93 94 Status = GpeWalkCallback (GpeXruptInfo, GpeBlock, Context); 95 if (ACPI_FAILURE (Status)) 96 { 97 if (Status == AE_CTRL_END) /* Callback abort */ 98 { 99 Status = AE_OK; 100 } 101 goto UnlockAndExit; 102 } 103 104 GpeBlock = GpeBlock->Next; 105 } 106 107 GpeXruptInfo = GpeXruptInfo->Next; 108 } 109 110 UnlockAndExit: 111 AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags); 112 return_ACPI_STATUS (Status); 113 } 114 115 116 /******************************************************************************* 117 * 118 * FUNCTION: AcpiEvValidGpeEvent 119 * 120 * PARAMETERS: GpeEventInfo - Info for this GPE 121 * 122 * RETURN: TRUE if the GpeEvent is valid 123 * 124 * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. 125 * Should be called only when the GPE lists are semaphore locked 126 * and not subject to change. 127 * 128 ******************************************************************************/ 129 130 BOOLEAN 131 AcpiEvValidGpeEvent ( 132 ACPI_GPE_EVENT_INFO *GpeEventInfo) 133 { 134 ACPI_GPE_XRUPT_INFO *GpeXruptBlock; 135 ACPI_GPE_BLOCK_INFO *GpeBlock; 136 137 138 ACPI_FUNCTION_ENTRY (); 139 140 141 /* No need for spin lock since we are not changing any list elements */ 142 143 /* Walk the GPE interrupt levels */ 144 145 GpeXruptBlock = AcpiGbl_GpeXruptListHead; 146 while (GpeXruptBlock) 147 { 148 GpeBlock = GpeXruptBlock->GpeBlockListHead; 149 150 /* Walk the GPE blocks on this interrupt level */ 151 152 while (GpeBlock) 153 { 154 if ((&GpeBlock->EventInfo[0] <= GpeEventInfo) && 155 (&GpeBlock->EventInfo[GpeBlock->GpeCount] > GpeEventInfo)) 156 { 157 return (TRUE); 158 } 159 160 GpeBlock = GpeBlock->Next; 161 } 162 163 GpeXruptBlock = GpeXruptBlock->Next; 164 } 165 166 return (FALSE); 167 } 168 169 170 /******************************************************************************* 171 * 172 * FUNCTION: AcpiEvGetGpeDevice 173 * 174 * PARAMETERS: GPE_WALK_CALLBACK 175 * 176 * RETURN: Status 177 * 178 * DESCRIPTION: Matches the input GPE index (0-CurrentGpeCount) with a GPE 179 * block device. NULL if the GPE is one of the FADT-defined GPEs. 180 * 181 ******************************************************************************/ 182 183 ACPI_STATUS 184 AcpiEvGetGpeDevice ( 185 ACPI_GPE_XRUPT_INFO *GpeXruptInfo, 186 ACPI_GPE_BLOCK_INFO *GpeBlock, 187 void *Context) 188 { 189 ACPI_GPE_DEVICE_INFO *Info = Context; 190 191 192 /* Increment Index by the number of GPEs in this block */ 193 194 Info->NextBlockBaseIndex += GpeBlock->GpeCount; 195 196 if (Info->Index < Info->NextBlockBaseIndex) 197 { 198 /* 199 * The GPE index is within this block, get the node. Leave the node 200 * NULL for the FADT-defined GPEs 201 */ 202 if ((GpeBlock->Node)->Type == ACPI_TYPE_DEVICE) 203 { 204 Info->GpeDevice = GpeBlock->Node; 205 } 206 207 Info->Status = AE_OK; 208 return (AE_CTRL_END); 209 } 210 211 return (AE_OK); 212 } 213 214 215 /******************************************************************************* 216 * 217 * FUNCTION: AcpiEvGetGpeXruptBlock 218 * 219 * PARAMETERS: InterruptNumber - Interrupt for a GPE block 220 * GpeXruptBlock - Where the block is returned 221 * 222 * RETURN: Status 223 * 224 * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt 225 * block per unique interrupt level used for GPEs. Should be 226 * called only when the GPE lists are semaphore locked and not 227 * subject to change. 228 * 229 ******************************************************************************/ 230 231 ACPI_STATUS 232 AcpiEvGetGpeXruptBlock ( 233 UINT32 InterruptNumber, 234 ACPI_GPE_XRUPT_INFO **GpeXruptBlock) 235 { 236 ACPI_GPE_XRUPT_INFO *NextGpeXrupt; 237 ACPI_GPE_XRUPT_INFO *GpeXrupt; 238 ACPI_STATUS Status; 239 ACPI_CPU_FLAGS Flags; 240 241 242 ACPI_FUNCTION_TRACE (EvGetGpeXruptBlock); 243 244 245 /* No need for lock since we are not changing any list elements here */ 246 247 NextGpeXrupt = AcpiGbl_GpeXruptListHead; 248 while (NextGpeXrupt) 249 { 250 if (NextGpeXrupt->InterruptNumber == InterruptNumber) 251 { 252 *GpeXruptBlock = NextGpeXrupt; 253 return_ACPI_STATUS (AE_OK); 254 } 255 256 NextGpeXrupt = NextGpeXrupt->Next; 257 } 258 259 /* Not found, must allocate a new xrupt descriptor */ 260 261 GpeXrupt = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_XRUPT_INFO)); 262 if (!GpeXrupt) 263 { 264 return_ACPI_STATUS (AE_NO_MEMORY); 265 } 266 267 GpeXrupt->InterruptNumber = InterruptNumber; 268 269 /* Install new interrupt descriptor with spin lock */ 270 271 Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock); 272 if (AcpiGbl_GpeXruptListHead) 273 { 274 NextGpeXrupt = AcpiGbl_GpeXruptListHead; 275 while (NextGpeXrupt->Next) 276 { 277 NextGpeXrupt = NextGpeXrupt->Next; 278 } 279 280 NextGpeXrupt->Next = GpeXrupt; 281 GpeXrupt->Previous = NextGpeXrupt; 282 } 283 else 284 { 285 AcpiGbl_GpeXruptListHead = GpeXrupt; 286 } 287 288 AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags); 289 290 /* Install new interrupt handler if not SCI_INT */ 291 292 if (InterruptNumber != AcpiGbl_FADT.SciInterrupt) 293 { 294 Status = AcpiOsInstallInterruptHandler (InterruptNumber, 295 AcpiEvGpeXruptHandler, GpeXrupt); 296 if (ACPI_FAILURE (Status)) 297 { 298 ACPI_EXCEPTION ((AE_INFO, Status, 299 "Could not install GPE interrupt handler at level 0x%X", 300 InterruptNumber)); 301 return_ACPI_STATUS (Status); 302 } 303 } 304 305 *GpeXruptBlock = GpeXrupt; 306 return_ACPI_STATUS (AE_OK); 307 } 308 309 310 /******************************************************************************* 311 * 312 * FUNCTION: AcpiEvDeleteGpeXrupt 313 * 314 * PARAMETERS: GpeXrupt - A GPE interrupt info block 315 * 316 * RETURN: Status 317 * 318 * DESCRIPTION: Remove and free a GpeXrupt block. Remove an associated 319 * interrupt handler if not the SCI interrupt. 320 * 321 ******************************************************************************/ 322 323 ACPI_STATUS 324 AcpiEvDeleteGpeXrupt ( 325 ACPI_GPE_XRUPT_INFO *GpeXrupt) 326 { 327 ACPI_STATUS Status; 328 ACPI_CPU_FLAGS Flags; 329 330 331 ACPI_FUNCTION_TRACE (EvDeleteGpeXrupt); 332 333 334 /* We never want to remove the SCI interrupt handler */ 335 336 if (GpeXrupt->InterruptNumber == AcpiGbl_FADT.SciInterrupt) 337 { 338 GpeXrupt->GpeBlockListHead = NULL; 339 return_ACPI_STATUS (AE_OK); 340 } 341 342 /* Disable this interrupt */ 343 344 Status = AcpiOsRemoveInterruptHandler ( 345 GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler); 346 if (ACPI_FAILURE (Status)) 347 { 348 return_ACPI_STATUS (Status); 349 } 350 351 /* Unlink the interrupt block with lock */ 352 353 Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock); 354 if (GpeXrupt->Previous) 355 { 356 GpeXrupt->Previous->Next = GpeXrupt->Next; 357 } 358 else 359 { 360 /* No previous, update list head */ 361 362 AcpiGbl_GpeXruptListHead = GpeXrupt->Next; 363 } 364 365 if (GpeXrupt->Next) 366 { 367 GpeXrupt->Next->Previous = GpeXrupt->Previous; 368 } 369 AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags); 370 371 /* Free the block */ 372 373 ACPI_FREE (GpeXrupt); 374 return_ACPI_STATUS (AE_OK); 375 } 376 377 378 /******************************************************************************* 379 * 380 * FUNCTION: AcpiEvDeleteGpeHandlers 381 * 382 * PARAMETERS: GpeXruptInfo - GPE Interrupt info 383 * GpeBlock - Gpe Block info 384 * 385 * RETURN: Status 386 * 387 * DESCRIPTION: Delete all Handler objects found in the GPE data structs. 388 * Used only prior to termination. 389 * 390 ******************************************************************************/ 391 392 ACPI_STATUS 393 AcpiEvDeleteGpeHandlers ( 394 ACPI_GPE_XRUPT_INFO *GpeXruptInfo, 395 ACPI_GPE_BLOCK_INFO *GpeBlock, 396 void *Context) 397 { 398 ACPI_GPE_EVENT_INFO *GpeEventInfo; 399 ACPI_GPE_NOTIFY_INFO *Notify; 400 ACPI_GPE_NOTIFY_INFO *Next; 401 UINT32 i; 402 UINT32 j; 403 404 405 ACPI_FUNCTION_TRACE (EvDeleteGpeHandlers); 406 407 408 /* Examine each GPE Register within the block */ 409 410 for (i = 0; i < GpeBlock->RegisterCount; i++) 411 { 412 /* Now look at the individual GPEs in this byte register */ 413 414 for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) 415 { 416 GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i * 417 ACPI_GPE_REGISTER_WIDTH) + j]; 418 419 if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) == 420 ACPI_GPE_DISPATCH_HANDLER) 421 { 422 /* Delete an installed handler block */ 423 424 ACPI_FREE (GpeEventInfo->Dispatch.Handler); 425 GpeEventInfo->Dispatch.Handler = NULL; 426 GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK; 427 } 428 else if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) == 429 ACPI_GPE_DISPATCH_NOTIFY) 430 { 431 /* Delete the implicit notification device list */ 432 433 Notify = GpeEventInfo->Dispatch.NotifyList; 434 while (Notify) 435 { 436 Next = Notify->Next; 437 ACPI_FREE (Notify); 438 Notify = Next; 439 } 440 GpeEventInfo->Dispatch.NotifyList = NULL; 441 GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK; 442 } 443 } 444 } 445 446 return_ACPI_STATUS (AE_OK); 447 } 448 449 #endif /* !ACPI_REDUCED_HARDWARE */