kernel: Sync ACPICA with Intel's version 20140114.
[dragonfly.git] / sys / contrib / dev / acpica / source / components / events / evgpeutil.c
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 */