1207340Sjkim/******************************************************************************
2207340Sjkim *
3207340Sjkim * Module Name: evgpeutil - GPE utilities
4207340Sjkim *
5207340Sjkim *****************************************************************************/
6207340Sjkim
7217365Sjkim/*
8245582Sjkim * Copyright (C) 2000 - 2013, Intel Corp.
9207340Sjkim * All rights reserved.
10207340Sjkim *
11217365Sjkim * Redistribution and use in source and binary forms, with or without
12217365Sjkim * modification, are permitted provided that the following conditions
13217365Sjkim * are met:
14217365Sjkim * 1. Redistributions of source code must retain the above copyright
15217365Sjkim *    notice, this list of conditions, and the following disclaimer,
16217365Sjkim *    without modification.
17217365Sjkim * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18217365Sjkim *    substantially similar to the "NO WARRANTY" disclaimer below
19217365Sjkim *    ("Disclaimer") and any redistribution must be conditioned upon
20217365Sjkim *    including a substantially similar Disclaimer requirement for further
21217365Sjkim *    binary redistribution.
22217365Sjkim * 3. Neither the names of the above-listed copyright holders nor the names
23217365Sjkim *    of any contributors may be used to endorse or promote products derived
24217365Sjkim *    from this software without specific prior written permission.
25207340Sjkim *
26217365Sjkim * Alternatively, this software may be distributed under the terms of the
27217365Sjkim * GNU General Public License ("GPL") version 2 as published by the Free
28217365Sjkim * Software Foundation.
29207340Sjkim *
30217365Sjkim * NO WARRANTY
31217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32217365Sjkim * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33217365Sjkim * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34217365Sjkim * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35217365Sjkim * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39217365Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40217365Sjkim * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41217365Sjkim * POSSIBILITY OF SUCH DAMAGES.
42217365Sjkim */
43207340Sjkim
44207344Sjkim#include <contrib/dev/acpica/include/acpi.h>
45207344Sjkim#include <contrib/dev/acpica/include/accommon.h>
46207344Sjkim#include <contrib/dev/acpica/include/acevents.h>
47207340Sjkim
48207340Sjkim#define _COMPONENT          ACPI_EVENTS
49207340Sjkim        ACPI_MODULE_NAME    ("evgpeutil")
50207340Sjkim
51207340Sjkim
52231844Sjkim#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
53207340Sjkim/*******************************************************************************
54207340Sjkim *
55207340Sjkim * FUNCTION:    AcpiEvWalkGpeList
56207340Sjkim *
57207340Sjkim * PARAMETERS:  GpeWalkCallback     - Routine called for each GPE block
58207340Sjkim *              Context             - Value passed to callback
59207340Sjkim *
60207340Sjkim * RETURN:      Status
61207340Sjkim *
62207340Sjkim * DESCRIPTION: Walk the GPE lists.
63207340Sjkim *
64207340Sjkim ******************************************************************************/
65207340Sjkim
66207340SjkimACPI_STATUS
67207340SjkimAcpiEvWalkGpeList (
68207340Sjkim    ACPI_GPE_CALLBACK       GpeWalkCallback,
69207340Sjkim    void                    *Context)
70207340Sjkim{
71207340Sjkim    ACPI_GPE_BLOCK_INFO     *GpeBlock;
72207340Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXruptInfo;
73207340Sjkim    ACPI_STATUS             Status = AE_OK;
74207340Sjkim    ACPI_CPU_FLAGS          Flags;
75207340Sjkim
76207340Sjkim
77207340Sjkim    ACPI_FUNCTION_TRACE (EvWalkGpeList);
78207340Sjkim
79207340Sjkim
80207340Sjkim    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
81207340Sjkim
82207340Sjkim    /* Walk the interrupt level descriptor list */
83207340Sjkim
84207340Sjkim    GpeXruptInfo = AcpiGbl_GpeXruptListHead;
85207340Sjkim    while (GpeXruptInfo)
86207340Sjkim    {
87207340Sjkim        /* Walk all Gpe Blocks attached to this interrupt level */
88207340Sjkim
89207340Sjkim        GpeBlock = GpeXruptInfo->GpeBlockListHead;
90207340Sjkim        while (GpeBlock)
91207340Sjkim        {
92207340Sjkim            /* One callback per GPE block */
93207340Sjkim
94207340Sjkim            Status = GpeWalkCallback (GpeXruptInfo, GpeBlock, Context);
95207340Sjkim            if (ACPI_FAILURE (Status))
96207340Sjkim            {
97207340Sjkim                if (Status == AE_CTRL_END) /* Callback abort */
98207340Sjkim                {
99207340Sjkim                    Status = AE_OK;
100207340Sjkim                }
101207340Sjkim                goto UnlockAndExit;
102207340Sjkim            }
103207340Sjkim
104207340Sjkim            GpeBlock = GpeBlock->Next;
105207340Sjkim        }
106207340Sjkim
107207340Sjkim        GpeXruptInfo = GpeXruptInfo->Next;
108207340Sjkim    }
109207340Sjkim
110207340SjkimUnlockAndExit:
111207340Sjkim    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
112207340Sjkim    return_ACPI_STATUS (Status);
113207340Sjkim}
114207340Sjkim
115207340Sjkim
116207340Sjkim/*******************************************************************************
117207340Sjkim *
118207340Sjkim * FUNCTION:    AcpiEvValidGpeEvent
119207340Sjkim *
120207340Sjkim * PARAMETERS:  GpeEventInfo                - Info for this GPE
121207340Sjkim *
122207340Sjkim * RETURN:      TRUE if the GpeEvent is valid
123207340Sjkim *
124207340Sjkim * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL.
125207340Sjkim *              Should be called only when the GPE lists are semaphore locked
126207340Sjkim *              and not subject to change.
127207340Sjkim *
128207340Sjkim ******************************************************************************/
129207340Sjkim
130207340SjkimBOOLEAN
131207340SjkimAcpiEvValidGpeEvent (
132207340Sjkim    ACPI_GPE_EVENT_INFO     *GpeEventInfo)
133207340Sjkim{
134207340Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXruptBlock;
135207340Sjkim    ACPI_GPE_BLOCK_INFO     *GpeBlock;
136207340Sjkim
137207340Sjkim
138207340Sjkim    ACPI_FUNCTION_ENTRY ();
139207340Sjkim
140207340Sjkim
141207340Sjkim    /* No need for spin lock since we are not changing any list elements */
142207340Sjkim
143207340Sjkim    /* Walk the GPE interrupt levels */
144207340Sjkim
145207340Sjkim    GpeXruptBlock = AcpiGbl_GpeXruptListHead;
146207340Sjkim    while (GpeXruptBlock)
147207340Sjkim    {
148207340Sjkim        GpeBlock = GpeXruptBlock->GpeBlockListHead;
149207340Sjkim
150207340Sjkim        /* Walk the GPE blocks on this interrupt level */
151207340Sjkim
152207340Sjkim        while (GpeBlock)
153207340Sjkim        {
154207340Sjkim            if ((&GpeBlock->EventInfo[0] <= GpeEventInfo) &&
155207340Sjkim                (&GpeBlock->EventInfo[GpeBlock->GpeCount] > GpeEventInfo))
156207340Sjkim            {
157207340Sjkim                return (TRUE);
158207340Sjkim            }
159207340Sjkim
160207340Sjkim            GpeBlock = GpeBlock->Next;
161207340Sjkim        }
162207340Sjkim
163207340Sjkim        GpeXruptBlock = GpeXruptBlock->Next;
164207340Sjkim    }
165207340Sjkim
166207340Sjkim    return (FALSE);
167207340Sjkim}
168207340Sjkim
169207340Sjkim
170207340Sjkim/*******************************************************************************
171207340Sjkim *
172216471Sjkim * FUNCTION:    AcpiEvGetGpeDevice
173216471Sjkim *
174216471Sjkim * PARAMETERS:  GPE_WALK_CALLBACK
175216471Sjkim *
176216471Sjkim * RETURN:      Status
177216471Sjkim *
178216471Sjkim * DESCRIPTION: Matches the input GPE index (0-CurrentGpeCount) with a GPE
179216471Sjkim *              block device. NULL if the GPE is one of the FADT-defined GPEs.
180216471Sjkim *
181216471Sjkim ******************************************************************************/
182216471Sjkim
183216471SjkimACPI_STATUS
184216471SjkimAcpiEvGetGpeDevice (
185216471Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
186216471Sjkim    ACPI_GPE_BLOCK_INFO     *GpeBlock,
187216471Sjkim    void                    *Context)
188216471Sjkim{
189216471Sjkim    ACPI_GPE_DEVICE_INFO    *Info = Context;
190216471Sjkim
191216471Sjkim
192216471Sjkim    /* Increment Index by the number of GPEs in this block */
193216471Sjkim
194216471Sjkim    Info->NextBlockBaseIndex += GpeBlock->GpeCount;
195216471Sjkim
196216471Sjkim    if (Info->Index < Info->NextBlockBaseIndex)
197216471Sjkim    {
198216471Sjkim        /*
199216471Sjkim         * The GPE index is within this block, get the node. Leave the node
200216471Sjkim         * NULL for the FADT-defined GPEs
201216471Sjkim         */
202216471Sjkim        if ((GpeBlock->Node)->Type == ACPI_TYPE_DEVICE)
203216471Sjkim        {
204216471Sjkim            Info->GpeDevice = GpeBlock->Node;
205216471Sjkim        }
206216471Sjkim
207216471Sjkim        Info->Status = AE_OK;
208216471Sjkim        return (AE_CTRL_END);
209216471Sjkim    }
210216471Sjkim
211216471Sjkim    return (AE_OK);
212216471Sjkim}
213216471Sjkim
214216471Sjkim
215216471Sjkim/*******************************************************************************
216216471Sjkim *
217207340Sjkim * FUNCTION:    AcpiEvGetGpeXruptBlock
218207340Sjkim *
219254745Sjkim * PARAMETERS:  InterruptNumber             - Interrupt for a GPE block
220207340Sjkim *
221207340Sjkim * RETURN:      A GPE interrupt block
222207340Sjkim *
223207340Sjkim * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
224207340Sjkim *              block per unique interrupt level used for GPEs. Should be
225207340Sjkim *              called only when the GPE lists are semaphore locked and not
226207340Sjkim *              subject to change.
227207340Sjkim *
228207340Sjkim ******************************************************************************/
229207340Sjkim
230207340SjkimACPI_GPE_XRUPT_INFO *
231207340SjkimAcpiEvGetGpeXruptBlock (
232207340Sjkim    UINT32                  InterruptNumber)
233207340Sjkim{
234207340Sjkim    ACPI_GPE_XRUPT_INFO     *NextGpeXrupt;
235207340Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXrupt;
236207340Sjkim    ACPI_STATUS             Status;
237207340Sjkim    ACPI_CPU_FLAGS          Flags;
238207340Sjkim
239207340Sjkim
240207340Sjkim    ACPI_FUNCTION_TRACE (EvGetGpeXruptBlock);
241207340Sjkim
242207340Sjkim
243207340Sjkim    /* No need for lock since we are not changing any list elements here */
244207340Sjkim
245207340Sjkim    NextGpeXrupt = AcpiGbl_GpeXruptListHead;
246207340Sjkim    while (NextGpeXrupt)
247207340Sjkim    {
248207340Sjkim        if (NextGpeXrupt->InterruptNumber == InterruptNumber)
249207340Sjkim        {
250207340Sjkim            return_PTR (NextGpeXrupt);
251207340Sjkim        }
252207340Sjkim
253207340Sjkim        NextGpeXrupt = NextGpeXrupt->Next;
254207340Sjkim    }
255207340Sjkim
256207340Sjkim    /* Not found, must allocate a new xrupt descriptor */
257207340Sjkim
258207340Sjkim    GpeXrupt = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_XRUPT_INFO));
259207340Sjkim    if (!GpeXrupt)
260207340Sjkim    {
261207340Sjkim        return_PTR (NULL);
262207340Sjkim    }
263207340Sjkim
264207340Sjkim    GpeXrupt->InterruptNumber = InterruptNumber;
265207340Sjkim
266207340Sjkim    /* Install new interrupt descriptor with spin lock */
267207340Sjkim
268207340Sjkim    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
269207340Sjkim    if (AcpiGbl_GpeXruptListHead)
270207340Sjkim    {
271207340Sjkim        NextGpeXrupt = AcpiGbl_GpeXruptListHead;
272207340Sjkim        while (NextGpeXrupt->Next)
273207340Sjkim        {
274207340Sjkim            NextGpeXrupt = NextGpeXrupt->Next;
275207340Sjkim        }
276207340Sjkim
277207340Sjkim        NextGpeXrupt->Next = GpeXrupt;
278207340Sjkim        GpeXrupt->Previous = NextGpeXrupt;
279207340Sjkim    }
280207340Sjkim    else
281207340Sjkim    {
282207340Sjkim        AcpiGbl_GpeXruptListHead = GpeXrupt;
283207340Sjkim    }
284207340Sjkim    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
285207340Sjkim
286207340Sjkim    /* Install new interrupt handler if not SCI_INT */
287207340Sjkim
288207340Sjkim    if (InterruptNumber != AcpiGbl_FADT.SciInterrupt)
289207340Sjkim    {
290207340Sjkim        Status = AcpiOsInstallInterruptHandler (InterruptNumber,
291207340Sjkim                    AcpiEvGpeXruptHandler, GpeXrupt);
292207340Sjkim        if (ACPI_FAILURE (Status))
293207340Sjkim        {
294207340Sjkim            ACPI_ERROR ((AE_INFO,
295207340Sjkim                "Could not install GPE interrupt handler at level 0x%X",
296207340Sjkim                InterruptNumber));
297207340Sjkim            return_PTR (NULL);
298207340Sjkim        }
299207340Sjkim    }
300207340Sjkim
301207340Sjkim    return_PTR (GpeXrupt);
302207340Sjkim}
303207340Sjkim
304207340Sjkim
305207340Sjkim/*******************************************************************************
306207340Sjkim *
307207340Sjkim * FUNCTION:    AcpiEvDeleteGpeXrupt
308207340Sjkim *
309207340Sjkim * PARAMETERS:  GpeXrupt        - A GPE interrupt info block
310207340Sjkim *
311207340Sjkim * RETURN:      Status
312207340Sjkim *
313207340Sjkim * DESCRIPTION: Remove and free a GpeXrupt block. Remove an associated
314207340Sjkim *              interrupt handler if not the SCI interrupt.
315207340Sjkim *
316207340Sjkim ******************************************************************************/
317207340Sjkim
318207340SjkimACPI_STATUS
319207340SjkimAcpiEvDeleteGpeXrupt (
320207340Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXrupt)
321207340Sjkim{
322207340Sjkim    ACPI_STATUS             Status;
323207340Sjkim    ACPI_CPU_FLAGS          Flags;
324207340Sjkim
325207340Sjkim
326207340Sjkim    ACPI_FUNCTION_TRACE (EvDeleteGpeXrupt);
327207340Sjkim
328207340Sjkim
329207340Sjkim    /* We never want to remove the SCI interrupt handler */
330207340Sjkim
331207340Sjkim    if (GpeXrupt->InterruptNumber == AcpiGbl_FADT.SciInterrupt)
332207340Sjkim    {
333207340Sjkim        GpeXrupt->GpeBlockListHead = NULL;
334207340Sjkim        return_ACPI_STATUS (AE_OK);
335207340Sjkim    }
336207340Sjkim
337207340Sjkim    /* Disable this interrupt */
338207340Sjkim
339207340Sjkim    Status = AcpiOsRemoveInterruptHandler (
340207340Sjkim                GpeXrupt->InterruptNumber, AcpiEvGpeXruptHandler);
341207340Sjkim    if (ACPI_FAILURE (Status))
342207340Sjkim    {
343207340Sjkim        return_ACPI_STATUS (Status);
344207340Sjkim    }
345207340Sjkim
346207340Sjkim    /* Unlink the interrupt block with lock */
347207340Sjkim
348207340Sjkim    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
349207340Sjkim    if (GpeXrupt->Previous)
350207340Sjkim    {
351207340Sjkim        GpeXrupt->Previous->Next = GpeXrupt->Next;
352207340Sjkim    }
353207340Sjkim    else
354207340Sjkim    {
355207340Sjkim        /* No previous, update list head */
356207340Sjkim
357207340Sjkim        AcpiGbl_GpeXruptListHead = GpeXrupt->Next;
358207340Sjkim    }
359207340Sjkim
360207340Sjkim    if (GpeXrupt->Next)
361207340Sjkim    {
362207340Sjkim        GpeXrupt->Next->Previous = GpeXrupt->Previous;
363207340Sjkim    }
364207340Sjkim    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
365207340Sjkim
366207340Sjkim    /* Free the block */
367207340Sjkim
368207340Sjkim    ACPI_FREE (GpeXrupt);
369207340Sjkim    return_ACPI_STATUS (AE_OK);
370207340Sjkim}
371207340Sjkim
372207340Sjkim
373207340Sjkim/*******************************************************************************
374207340Sjkim *
375207340Sjkim * FUNCTION:    AcpiEvDeleteGpeHandlers
376207340Sjkim *
377207340Sjkim * PARAMETERS:  GpeXruptInfo        - GPE Interrupt info
378207340Sjkim *              GpeBlock            - Gpe Block info
379207340Sjkim *
380207340Sjkim * RETURN:      Status
381207340Sjkim *
382207340Sjkim * DESCRIPTION: Delete all Handler objects found in the GPE data structs.
383207340Sjkim *              Used only prior to termination.
384207340Sjkim *
385207340Sjkim ******************************************************************************/
386207340Sjkim
387207340SjkimACPI_STATUS
388207340SjkimAcpiEvDeleteGpeHandlers (
389207340Sjkim    ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
390207340Sjkim    ACPI_GPE_BLOCK_INFO     *GpeBlock,
391207340Sjkim    void                    *Context)
392207340Sjkim{
393207340Sjkim    ACPI_GPE_EVENT_INFO     *GpeEventInfo;
394237412Sjkim    ACPI_GPE_NOTIFY_INFO    *Notify;
395237412Sjkim    ACPI_GPE_NOTIFY_INFO    *Next;
396207340Sjkim    UINT32                  i;
397207340Sjkim    UINT32                  j;
398207340Sjkim
399207340Sjkim
400207340Sjkim    ACPI_FUNCTION_TRACE (EvDeleteGpeHandlers);
401207340Sjkim
402207340Sjkim
403207340Sjkim    /* Examine each GPE Register within the block */
404207340Sjkim
405207340Sjkim    for (i = 0; i < GpeBlock->RegisterCount; i++)
406207340Sjkim    {
407207340Sjkim        /* Now look at the individual GPEs in this byte register */
408207340Sjkim
409207340Sjkim        for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
410207340Sjkim        {
411207340Sjkim            GpeEventInfo = &GpeBlock->EventInfo[((ACPI_SIZE) i *
412207340Sjkim                ACPI_GPE_REGISTER_WIDTH) + j];
413207340Sjkim
414207340Sjkim            if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
415207340Sjkim                    ACPI_GPE_DISPATCH_HANDLER)
416207340Sjkim            {
417237412Sjkim                /* Delete an installed handler block */
418237412Sjkim
419207340Sjkim                ACPI_FREE (GpeEventInfo->Dispatch.Handler);
420207340Sjkim                GpeEventInfo->Dispatch.Handler = NULL;
421207340Sjkim                GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
422207340Sjkim            }
423237412Sjkim            else if ((GpeEventInfo->Flags & ACPI_GPE_DISPATCH_MASK) ==
424237412Sjkim                    ACPI_GPE_DISPATCH_NOTIFY)
425237412Sjkim            {
426237412Sjkim                /* Delete the implicit notification device list */
427237412Sjkim
428237412Sjkim                Notify = GpeEventInfo->Dispatch.NotifyList;
429237412Sjkim                while (Notify)
430237412Sjkim                {
431237412Sjkim                    Next = Notify->Next;
432237412Sjkim                    ACPI_FREE (Notify);
433237412Sjkim                    Notify = Next;
434237412Sjkim                }
435237412Sjkim                GpeEventInfo->Dispatch.NotifyList = NULL;
436237412Sjkim                GpeEventInfo->Flags &= ~ACPI_GPE_DISPATCH_MASK;
437237412Sjkim            }
438207340Sjkim        }
439207340Sjkim    }
440207340Sjkim
441207340Sjkim    return_ACPI_STATUS (AE_OK);
442207340Sjkim}
443207340Sjkim
444231844Sjkim#endif /* !ACPI_REDUCED_HARDWARE */
445