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 */
450