evgpeutil.c revision 306536
1/******************************************************************************
2 *
3 * Module Name: evgpeutil - GPE utilities
4 *
5 *****************************************************************************/
6
7/*
8 * Copyright (C) 2000 - 2016, 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:    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
129ACPI_STATUS
130AcpiEvGetGpeDevice (
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
177ACPI_STATUS
178AcpiEvGetGpeXruptBlock (
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
269ACPI_STATUS
270AcpiEvDeleteGpeXrupt (
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
338ACPI_STATUS
339AcpiEvDeleteGpeHandlers (
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
389                GpeEventInfo->Dispatch.NotifyList = NULL;
390                GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
391            }
392        }
393    }
394
395    return_ACPI_STATUS (AE_OK);
396}
397
398#endif /* !ACPI_REDUCED_HARDWARE */
399