xref: /freebsd/sys/contrib/dev/acpica/components/events/evgpeutil.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
1 /******************************************************************************
2  *
3  * Module Name: evgpeutil - GPE utilities
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2015, 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 <contrib/dev/acpica/include/acpi.h>
45 #include <contrib/dev/acpica/include/accommon.h>
46 #include <contrib/dev/acpica/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:    AcpiEvGetGpeDevice
119  *
120  * PARAMETERS:  GPE_WALK_CALLBACK
121  *
122  * RETURN:      Status
123  *
124  * DESCRIPTION: Matches the input GPE index (0-CurrentGpeCount) with a GPE
125  *              block device. NULL if the GPE is one of the FADT-defined GPEs.
126  *
127  ******************************************************************************/
128 
129 ACPI_STATUS
130 AcpiEvGetGpeDevice (
131     ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
132     ACPI_GPE_BLOCK_INFO     *GpeBlock,
133     void                    *Context)
134 {
135     ACPI_GPE_DEVICE_INFO    *Info = Context;
136 
137 
138     /* Increment Index by the number of GPEs in this block */
139 
140     Info->NextBlockBaseIndex += GpeBlock->GpeCount;
141 
142     if (Info->Index < Info->NextBlockBaseIndex)
143     {
144         /*
145          * The GPE index is within this block, get the node. Leave the node
146          * NULL for the FADT-defined GPEs
147          */
148         if ((GpeBlock->Node)->Type == ACPI_TYPE_DEVICE)
149         {
150             Info->GpeDevice = GpeBlock->Node;
151         }
152 
153         Info->Status = AE_OK;
154         return (AE_CTRL_END);
155     }
156 
157     return (AE_OK);
158 }
159 
160 
161 /*******************************************************************************
162  *
163  * FUNCTION:    AcpiEvGetGpeXruptBlock
164  *
165  * PARAMETERS:  InterruptNumber             - Interrupt for a GPE block
166  *              GpeXruptBlock               - Where the block is returned
167  *
168  * RETURN:      Status
169  *
170  * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
171  *              block per unique interrupt level used for GPEs. Should be
172  *              called only when the GPE lists are semaphore locked and not
173  *              subject to change.
174  *
175  ******************************************************************************/
176 
177 ACPI_STATUS
178 AcpiEvGetGpeXruptBlock (
179     UINT32                  InterruptNumber,
180     ACPI_GPE_XRUPT_INFO     **GpeXruptBlock)
181 {
182     ACPI_GPE_XRUPT_INFO     *NextGpeXrupt;
183     ACPI_GPE_XRUPT_INFO     *GpeXrupt;
184     ACPI_STATUS             Status;
185     ACPI_CPU_FLAGS          Flags;
186 
187 
188     ACPI_FUNCTION_TRACE (EvGetGpeXruptBlock);
189 
190 
191     /* No need for lock since we are not changing any list elements here */
192 
193     NextGpeXrupt = AcpiGbl_GpeXruptListHead;
194     while (NextGpeXrupt)
195     {
196         if (NextGpeXrupt->InterruptNumber == InterruptNumber)
197         {
198             *GpeXruptBlock = NextGpeXrupt;
199             return_ACPI_STATUS (AE_OK);
200         }
201 
202         NextGpeXrupt = NextGpeXrupt->Next;
203     }
204 
205     /* Not found, must allocate a new xrupt descriptor */
206 
207     GpeXrupt = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_XRUPT_INFO));
208     if (!GpeXrupt)
209     {
210         return_ACPI_STATUS (AE_NO_MEMORY);
211     }
212 
213     GpeXrupt->InterruptNumber = InterruptNumber;
214 
215     /* Install new interrupt descriptor with spin lock */
216 
217     Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
218     if (AcpiGbl_GpeXruptListHead)
219     {
220         NextGpeXrupt = AcpiGbl_GpeXruptListHead;
221         while (NextGpeXrupt->Next)
222         {
223             NextGpeXrupt = NextGpeXrupt->Next;
224         }
225 
226         NextGpeXrupt->Next = GpeXrupt;
227         GpeXrupt->Previous = NextGpeXrupt;
228     }
229     else
230     {
231         AcpiGbl_GpeXruptListHead = GpeXrupt;
232     }
233 
234     AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
235 
236     /* Install new interrupt handler if not SCI_INT */
237 
238     if (InterruptNumber != AcpiGbl_FADT.SciInterrupt)
239     {
240         Status = AcpiOsInstallInterruptHandler (InterruptNumber,
241                     AcpiEvGpeXruptHandler, GpeXrupt);
242         if (ACPI_FAILURE (Status))
243         {
244             ACPI_EXCEPTION ((AE_INFO, Status,
245                 "Could not install GPE interrupt handler at level 0x%X",
246                 InterruptNumber));
247             return_ACPI_STATUS (Status);
248         }
249     }
250 
251     *GpeXruptBlock = GpeXrupt;
252     return_ACPI_STATUS (AE_OK);
253 }
254 
255 
256 /*******************************************************************************
257  *
258  * FUNCTION:    AcpiEvDeleteGpeXrupt
259  *
260  * PARAMETERS:  GpeXrupt        - A GPE interrupt info block
261  *
262  * RETURN:      Status
263  *
264  * DESCRIPTION: Remove and free a GpeXrupt block. Remove an associated
265  *              interrupt handler if not the SCI interrupt.
266  *
267  ******************************************************************************/
268 
269 ACPI_STATUS
270 AcpiEvDeleteGpeXrupt (
271     ACPI_GPE_XRUPT_INFO     *GpeXrupt)
272 {
273     ACPI_STATUS             Status;
274     ACPI_CPU_FLAGS          Flags;
275 
276 
277     ACPI_FUNCTION_TRACE (EvDeleteGpeXrupt);
278 
279 
280     /* We never want to remove the SCI interrupt handler */
281 
282     if (GpeXrupt->InterruptNumber == AcpiGbl_FADT.SciInterrupt)
283     {
284         GpeXrupt->GpeBlockListHead = NULL;
285         return_ACPI_STATUS (AE_OK);
286     }
287 
288     /* Disable this interrupt */
289 
290     Status = AcpiOsRemoveInterruptHandler (
291                 GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler);
292     if (ACPI_FAILURE (Status))
293     {
294         return_ACPI_STATUS (Status);
295     }
296 
297     /* Unlink the interrupt block with lock */
298 
299     Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
300     if (GpeXrupt->Previous)
301     {
302         GpeXrupt->Previous->Next = GpeXrupt->Next;
303     }
304     else
305     {
306         /* No previous, update list head */
307 
308         AcpiGbl_GpeXruptListHead = GpeXrupt->Next;
309     }
310 
311     if (GpeXrupt->Next)
312     {
313         GpeXrupt->Next->Previous = GpeXrupt->Previous;
314     }
315     AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
316 
317     /* Free the block */
318 
319     ACPI_FREE (GpeXrupt);
320     return_ACPI_STATUS (AE_OK);
321 }
322 
323 
324 /*******************************************************************************
325  *
326  * FUNCTION:    AcpiEvDeleteGpeHandlers
327  *
328  * PARAMETERS:  GpeXruptInfo        - GPE Interrupt info
329  *              GpeBlock            - Gpe Block info
330  *
331  * RETURN:      Status
332  *
333  * DESCRIPTION: Delete all Handler objects found in the GPE data structs.
334  *              Used only prior to termination.
335  *
336  ******************************************************************************/
337 
338 ACPI_STATUS
339 AcpiEvDeleteGpeHandlers (
340     ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
341     ACPI_GPE_BLOCK_INFO     *GpeBlock,
342     void                    *Context)
343 {
344     ACPI_GPE_EVENT_INFO     *GpeEventInfo;
345     ACPI_GPE_NOTIFY_INFO    *Notify;
346     ACPI_GPE_NOTIFY_INFO    *Next;
347     UINT32                  i;
348     UINT32                  j;
349 
350 
351     ACPI_FUNCTION_TRACE (EvDeleteGpeHandlers);
352 
353 
354     /* Examine each GPE Register within the block */
355 
356     for (i = 0; i < GpeBlock->RegisterCount; i++)
357     {
358         /* Now look at the individual GPEs in this byte register */
359 
360         for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
361         {
362             GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i *
363                 ACPI_GPE_REGISTER_WIDTH) + j];
364 
365             if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
366                     ACPI_GPE_DISPATCH_HANDLER) ||
367                 (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
368                     ACPI_GPE_DISPATCH_RAW_HANDLER))
369             {
370                 /* Delete an installed handler block */
371 
372                 ACPI_FREE (GpeEventInfo->Dispatch.Handler);
373                 GpeEventInfo->Dispatch.Handler = NULL;
374                 GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
375             }
376             else if (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) ==
377                     ACPI_GPE_DISPATCH_NOTIFY)
378             {
379                 /* Delete the implicit notification device list */
380 
381                 Notify = GpeEventInfo->Dispatch.NotifyList;
382                 while (Notify)
383                 {
384                     Next = Notify->Next;
385                     ACPI_FREE (Notify);
386                     Notify = Next;
387                 }
388                 GpeEventInfo->Dispatch.NotifyList = NULL;
389                 GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
390             }
391         }
392     }
393 
394     return_ACPI_STATUS (AE_OK);
395 }
396 
397 #endif /* !ACPI_REDUCED_HARDWARE */
398