evgpeutil.c revision 237412
1/******************************************************************************
2 *
3 * Module Name: evgpeutil - GPE utilities
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2012, 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
66ACPI_STATUS
67AcpiEvWalkGpeList (
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
110UnlockAndExit:
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
130BOOLEAN
131AcpiEvValidGpeEvent (
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
183ACPI_STATUS
184AcpiEvGetGpeDevice (
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 *
221 * RETURN:      A GPE interrupt block
222 *
223 * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
224 *              block per unique interrupt level used for GPEs. Should be
225 *              called only when the GPE lists are semaphore locked and not
226 *              subject to change.
227 *
228 ******************************************************************************/
229
230ACPI_GPE_XRUPT_INFO *
231AcpiEvGetGpeXruptBlock (
232    UINT32                  InterruptNumber)
233{
234    ACPI_GPE_XRUPT_INFO     *NextGpeXrupt;
235    ACPI_GPE_XRUPT_INFO     *GpeXrupt;
236    ACPI_STATUS             Status;
237    ACPI_CPU_FLAGS          Flags;
238
239
240    ACPI_FUNCTION_TRACE (EvGetGpeXruptBlock);
241
242
243    /* No need for lock since we are not changing any list elements here */
244
245    NextGpeXrupt = AcpiGbl_GpeXruptListHead;
246    while (NextGpeXrupt)
247    {
248        if (NextGpeXrupt->InterruptNumber == InterruptNumber)
249        {
250            return_PTR (NextGpeXrupt);
251        }
252
253        NextGpeXrupt = NextGpeXrupt->Next;
254    }
255
256    /* Not found, must allocate a new xrupt descriptor */
257
258    GpeXrupt = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_XRUPT_INFO));
259    if (!GpeXrupt)
260    {
261        return_PTR (NULL);
262    }
263
264    GpeXrupt->InterruptNumber = InterruptNumber;
265
266    /* Install new interrupt descriptor with spin lock */
267
268    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
269    if (AcpiGbl_GpeXruptListHead)
270    {
271        NextGpeXrupt = AcpiGbl_GpeXruptListHead;
272        while (NextGpeXrupt->Next)
273        {
274            NextGpeXrupt = NextGpeXrupt->Next;
275        }
276
277        NextGpeXrupt->Next = GpeXrupt;
278        GpeXrupt->Previous = NextGpeXrupt;
279    }
280    else
281    {
282        AcpiGbl_GpeXruptListHead = GpeXrupt;
283    }
284    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
285
286    /* Install new interrupt handler if not SCI_INT */
287
288    if (InterruptNumber != AcpiGbl_FADT.SciInterrupt)
289    {
290        Status = AcpiOsInstallInterruptHandler (InterruptNumber,
291                    AcpiEvGpeXruptHandler, GpeXrupt);
292        if (ACPI_FAILURE (Status))
293        {
294            ACPI_ERROR ((AE_INFO,
295                "Could not install GPE interrupt handler at level 0x%X",
296                InterruptNumber));
297            return_PTR (NULL);
298        }
299    }
300
301    return_PTR (GpeXrupt);
302}
303
304
305/*******************************************************************************
306 *
307 * FUNCTION:    AcpiEvDeleteGpeXrupt
308 *
309 * PARAMETERS:  GpeXrupt        - A GPE interrupt info block
310 *
311 * RETURN:      Status
312 *
313 * DESCRIPTION: Remove and free a GpeXrupt block. Remove an associated
314 *              interrupt handler if not the SCI interrupt.
315 *
316 ******************************************************************************/
317
318ACPI_STATUS
319AcpiEvDeleteGpeXrupt (
320    ACPI_GPE_XRUPT_INFO     *GpeXrupt)
321{
322    ACPI_STATUS             Status;
323    ACPI_CPU_FLAGS          Flags;
324
325
326    ACPI_FUNCTION_TRACE (EvDeleteGpeXrupt);
327
328
329    /* We never want to remove the SCI interrupt handler */
330
331    if (GpeXrupt->InterruptNumber == AcpiGbl_FADT.SciInterrupt)
332    {
333        GpeXrupt->GpeBlockListHead = NULL;
334        return_ACPI_STATUS (AE_OK);
335    }
336
337    /* Disable this interrupt */
338
339    Status = AcpiOsRemoveInterruptHandler (
340                GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler);
341    if (ACPI_FAILURE (Status))
342    {
343        return_ACPI_STATUS (Status);
344    }
345
346    /* Unlink the interrupt block with lock */
347
348    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
349    if (GpeXrupt->Previous)
350    {
351        GpeXrupt->Previous->Next = GpeXrupt->Next;
352    }
353    else
354    {
355        /* No previous, update list head */
356
357        AcpiGbl_GpeXruptListHead = GpeXrupt->Next;
358    }
359
360    if (GpeXrupt->Next)
361    {
362        GpeXrupt->Next->Previous = GpeXrupt->Previous;
363    }
364    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
365
366    /* Free the block */
367
368    ACPI_FREE (GpeXrupt);
369    return_ACPI_STATUS (AE_OK);
370}
371
372
373/*******************************************************************************
374 *
375 * FUNCTION:    AcpiEvDeleteGpeHandlers
376 *
377 * PARAMETERS:  GpeXruptInfo        - GPE Interrupt info
378 *              GpeBlock            - Gpe Block info
379 *
380 * RETURN:      Status
381 *
382 * DESCRIPTION: Delete all Handler objects found in the GPE data structs.
383 *              Used only prior to termination.
384 *
385 ******************************************************************************/
386
387ACPI_STATUS
388AcpiEvDeleteGpeHandlers (
389    ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
390    ACPI_GPE_BLOCK_INFO     *GpeBlock,
391    void                    *Context)
392{
393    ACPI_GPE_EVENT_INFO     *GpeEventInfo;
394    ACPI_GPE_NOTIFY_INFO    *Notify;
395    ACPI_GPE_NOTIFY_INFO    *Next;
396    UINT32                  i;
397    UINT32                  j;
398
399
400    ACPI_FUNCTION_TRACE (EvDeleteGpeHandlers);
401
402
403    /* Examine each GPE Register within the block */
404
405    for (i = 0; i < GpeBlock->RegisterCount; i++)
406    {
407        /* Now look at the individual GPEs in this byte register */
408
409        for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
410        {
411            GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i *
412                ACPI_GPE_REGISTER_WIDTH) + j];
413
414            if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
415                    ACPI_GPE_DISPATCH_HANDLER)
416            {
417                /* Delete an installed handler block */
418
419                ACPI_FREE (GpeEventInfo->Dispatch.Handler);
420                GpeEventInfo->Dispatch.Handler = NULL;
421                GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
422            }
423            else if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
424                    ACPI_GPE_DISPATCH_NOTIFY)
425            {
426                /* Delete the implicit notification device list */
427
428                Notify = GpeEventInfo->Dispatch.NotifyList;
429                while (Notify)
430                {
431                    Next = Notify->Next;
432                    ACPI_FREE (Notify);
433                    Notify = Next;
434                }
435                GpeEventInfo->Dispatch.NotifyList = NULL;
436                GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
437            }
438        }
439    }
440
441    return_ACPI_STATUS (AE_OK);
442}
443
444#endif /* !ACPI_REDUCED_HARDWARE */
445