1/******************************************************************************
2 *
3 * Module Name: evgpeblk - GPE block creation and initialization.
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#include <contrib/dev/acpica/include/acnamesp.h>
48
49#define _COMPONENT          ACPI_EVENTS
50        ACPI_MODULE_NAME    ("evgpeblk")
51
52#if (!ACPI_REDUCED_HARDWARE) /* Entire module */
53
54/* Local prototypes */
55
56static ACPI_STATUS
57AcpiEvInstallGpeBlock (
58    ACPI_GPE_BLOCK_INFO     *GpeBlock,
59    UINT32                  InterruptNumber);
60
61static ACPI_STATUS
62AcpiEvCreateGpeInfoBlocks (
63    ACPI_GPE_BLOCK_INFO     *GpeBlock);
64
65
66/*******************************************************************************
67 *
68 * FUNCTION:    AcpiEvInstallGpeBlock
69 *
70 * PARAMETERS:  GpeBlock                - New GPE block
71 *              InterruptNumber         - Xrupt to be associated with this
72 *                                        GPE block
73 *
74 * RETURN:      Status
75 *
76 * DESCRIPTION: Install new GPE block with mutex support
77 *
78 ******************************************************************************/
79
80static ACPI_STATUS
81AcpiEvInstallGpeBlock (
82    ACPI_GPE_BLOCK_INFO     *GpeBlock,
83    UINT32                  InterruptNumber)
84{
85    ACPI_GPE_BLOCK_INFO     *NextGpeBlock;
86    ACPI_GPE_XRUPT_INFO     *GpeXruptBlock;
87    ACPI_STATUS             Status;
88    ACPI_CPU_FLAGS          Flags;
89
90
91    ACPI_FUNCTION_TRACE (EvInstallGpeBlock);
92
93
94    Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
95    if (ACPI_FAILURE (Status))
96    {
97        return_ACPI_STATUS (Status);
98    }
99
100    Status = AcpiEvGetGpeXruptBlock (InterruptNumber, &GpeXruptBlock);
101    if (ACPI_FAILURE (Status))
102    {
103        goto UnlockAndExit;
104    }
105
106    /* Install the new block at the end of the list with lock */
107
108    Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
109    if (GpeXruptBlock->GpeBlockListHead)
110    {
111        NextGpeBlock = GpeXruptBlock->GpeBlockListHead;
112        while (NextGpeBlock->Next)
113        {
114            NextGpeBlock = NextGpeBlock->Next;
115        }
116
117        NextGpeBlock->Next = GpeBlock;
118        GpeBlock->Previous = NextGpeBlock;
119    }
120    else
121    {
122        GpeXruptBlock->GpeBlockListHead = GpeBlock;
123    }
124
125    GpeBlock->XruptBlock = GpeXruptBlock;
126    AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
127
128
129UnlockAndExit:
130    (void) AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
131    return_ACPI_STATUS (Status);
132}
133
134
135/*******************************************************************************
136 *
137 * FUNCTION:    AcpiEvDeleteGpeBlock
138 *
139 * PARAMETERS:  GpeBlock            - Existing GPE block
140 *
141 * RETURN:      Status
142 *
143 * DESCRIPTION: Remove a GPE block
144 *
145 ******************************************************************************/
146
147ACPI_STATUS
148AcpiEvDeleteGpeBlock (
149    ACPI_GPE_BLOCK_INFO     *GpeBlock)
150{
151    ACPI_STATUS             Status;
152    ACPI_CPU_FLAGS          Flags;
153
154
155    ACPI_FUNCTION_TRACE (EvInstallGpeBlock);
156
157
158    Status = AcpiUtAcquireMutex (ACPI_MTX_EVENTS);
159    if (ACPI_FAILURE (Status))
160    {
161        return_ACPI_STATUS (Status);
162    }
163
164    /* Disable all GPEs in this block */
165
166    Status = AcpiHwDisableGpeBlock (GpeBlock->XruptBlock, GpeBlock, NULL);
167
168    if (!GpeBlock->Previous && !GpeBlock->Next)
169    {
170        /* This is the last GpeBlock on this interrupt */
171
172        Status = AcpiEvDeleteGpeXrupt (GpeBlock->XruptBlock);
173        if (ACPI_FAILURE (Status))
174        {
175            goto UnlockAndExit;
176        }
177    }
178    else
179    {
180        /* Remove the block on this interrupt with lock */
181
182        Flags = AcpiOsAcquireLock (AcpiGbl_GpeLock);
183        if (GpeBlock->Previous)
184        {
185            GpeBlock->Previous->Next = GpeBlock->Next;
186        }
187        else
188        {
189            GpeBlock->XruptBlock->GpeBlockListHead = GpeBlock->Next;
190        }
191
192        if (GpeBlock->Next)
193        {
194            GpeBlock->Next->Previous = GpeBlock->Previous;
195        }
196
197        AcpiOsReleaseLock (AcpiGbl_GpeLock, Flags);
198    }
199
200    AcpiCurrentGpeCount -= GpeBlock->GpeCount;
201
202    /* Free the GpeBlock */
203
204    ACPI_FREE (GpeBlock->RegisterInfo);
205    ACPI_FREE (GpeBlock->EventInfo);
206    ACPI_FREE (GpeBlock);
207
208UnlockAndExit:
209    Status = AcpiUtReleaseMutex (ACPI_MTX_EVENTS);
210    return_ACPI_STATUS (Status);
211}
212
213
214/*******************************************************************************
215 *
216 * FUNCTION:    AcpiEvCreateGpeInfoBlocks
217 *
218 * PARAMETERS:  GpeBlock    - New GPE block
219 *
220 * RETURN:      Status
221 *
222 * DESCRIPTION: Create the RegisterInfo and EventInfo blocks for this GPE block
223 *
224 ******************************************************************************/
225
226static ACPI_STATUS
227AcpiEvCreateGpeInfoBlocks (
228    ACPI_GPE_BLOCK_INFO     *GpeBlock)
229{
230    ACPI_GPE_REGISTER_INFO  *GpeRegisterInfo = NULL;
231    ACPI_GPE_EVENT_INFO     *GpeEventInfo = NULL;
232    ACPI_GPE_EVENT_INFO     *ThisEvent;
233    ACPI_GPE_REGISTER_INFO  *ThisRegister;
234    UINT32                  i;
235    UINT32                  j;
236    ACPI_STATUS             Status;
237
238
239    ACPI_FUNCTION_TRACE (EvCreateGpeInfoBlocks);
240
241
242    /* Allocate the GPE register information block */
243
244    GpeRegisterInfo = ACPI_ALLOCATE_ZEROED (
245        (ACPI_SIZE) GpeBlock->RegisterCount *
246        sizeof (ACPI_GPE_REGISTER_INFO));
247    if (!GpeRegisterInfo)
248    {
249        ACPI_ERROR ((AE_INFO,
250            "Could not allocate the GpeRegisterInfo table"));
251        return_ACPI_STATUS (AE_NO_MEMORY);
252    }
253
254    /*
255     * Allocate the GPE EventInfo block. There are eight distinct GPEs
256     * per register. Initialization to zeros is sufficient.
257     */
258    GpeEventInfo = ACPI_ALLOCATE_ZEROED ((ACPI_SIZE) GpeBlock->GpeCount *
259        sizeof (ACPI_GPE_EVENT_INFO));
260    if (!GpeEventInfo)
261    {
262        ACPI_ERROR ((AE_INFO,
263            "Could not allocate the GpeEventInfo table"));
264        Status = AE_NO_MEMORY;
265        goto ErrorExit;
266    }
267
268    /* Save the new Info arrays in the GPE block */
269
270    GpeBlock->RegisterInfo = GpeRegisterInfo;
271    GpeBlock->EventInfo = GpeEventInfo;
272
273    /*
274     * Initialize the GPE Register and Event structures. A goal of these
275     * tables is to hide the fact that there are two separate GPE register
276     * sets in a given GPE hardware block, the status registers occupy the
277     * first half, and the enable registers occupy the second half.
278     */
279    ThisRegister = GpeRegisterInfo;
280    ThisEvent = GpeEventInfo;
281
282    for (i = 0; i < GpeBlock->RegisterCount; i++)
283    {
284        /* Init the RegisterInfo for this GPE register (8 GPEs) */
285
286        ThisRegister->BaseGpeNumber = (UINT16)
287            (GpeBlock->BlockBaseNumber + (i * ACPI_GPE_REGISTER_WIDTH));
288
289        ThisRegister->StatusAddress.Address =
290            GpeBlock->Address + i;
291
292        ThisRegister->EnableAddress.Address =
293            GpeBlock->Address + i + GpeBlock->RegisterCount;
294
295        ThisRegister->StatusAddress.SpaceId   = GpeBlock->SpaceId;
296        ThisRegister->EnableAddress.SpaceId   = GpeBlock->SpaceId;
297        ThisRegister->StatusAddress.BitWidth  = ACPI_GPE_REGISTER_WIDTH;
298        ThisRegister->EnableAddress.BitWidth  = ACPI_GPE_REGISTER_WIDTH;
299        ThisRegister->StatusAddress.BitOffset = 0;
300        ThisRegister->EnableAddress.BitOffset = 0;
301
302        /* Init the EventInfo for each GPE within this register */
303
304        for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
305        {
306            ThisEvent->GpeNumber = (UINT8) (ThisRegister->BaseGpeNumber + j);
307            ThisEvent->RegisterInfo = ThisRegister;
308            ThisEvent++;
309        }
310
311        /* Disable all GPEs within this register */
312
313        Status = AcpiHwWrite (0x00, &ThisRegister->EnableAddress);
314        if (ACPI_FAILURE (Status))
315        {
316            goto ErrorExit;
317        }
318
319        /* Clear any pending GPE events within this register */
320
321        Status = AcpiHwWrite (0xFF, &ThisRegister->StatusAddress);
322        if (ACPI_FAILURE (Status))
323        {
324            goto ErrorExit;
325        }
326
327        ThisRegister++;
328    }
329
330    return_ACPI_STATUS (AE_OK);
331
332
333ErrorExit:
334    if (GpeRegisterInfo)
335    {
336        ACPI_FREE (GpeRegisterInfo);
337    }
338    if (GpeEventInfo)
339    {
340        ACPI_FREE (GpeEventInfo);
341    }
342
343    return_ACPI_STATUS (Status);
344}
345
346
347/*******************************************************************************
348 *
349 * FUNCTION:    AcpiEvCreateGpeBlock
350 *
351 * PARAMETERS:  GpeDevice           - Handle to the parent GPE block
352 *              GpeBlockAddress     - Address and SpaceID
353 *              RegisterCount       - Number of GPE register pairs in the block
354 *              GpeBlockBaseNumber  - Starting GPE number for the block
355 *              InterruptNumber     - H/W interrupt for the block
356 *              ReturnGpeBlock      - Where the new block descriptor is returned
357 *
358 * RETURN:      Status
359 *
360 * DESCRIPTION: Create and Install a block of GPE registers. All GPEs within
361 *              the block are disabled at exit.
362 *              Note: Assumes namespace is locked.
363 *
364 ******************************************************************************/
365
366ACPI_STATUS
367AcpiEvCreateGpeBlock (
368    ACPI_NAMESPACE_NODE     *GpeDevice,
369    UINT64                  Address,
370    UINT8                   SpaceId,
371    UINT32                  RegisterCount,
372    UINT16                  GpeBlockBaseNumber,
373    UINT32                  InterruptNumber,
374    ACPI_GPE_BLOCK_INFO     **ReturnGpeBlock)
375{
376    ACPI_STATUS             Status;
377    ACPI_GPE_BLOCK_INFO     *GpeBlock;
378    ACPI_GPE_WALK_INFO      WalkInfo;
379
380
381    ACPI_FUNCTION_TRACE (EvCreateGpeBlock);
382
383
384    if (!RegisterCount)
385    {
386        return_ACPI_STATUS (AE_OK);
387    }
388
389    /* Allocate a new GPE block */
390
391    GpeBlock = ACPI_ALLOCATE_ZEROED (sizeof (ACPI_GPE_BLOCK_INFO));
392    if (!GpeBlock)
393    {
394        return_ACPI_STATUS (AE_NO_MEMORY);
395    }
396
397    /* Initialize the new GPE block */
398
399    GpeBlock->Address = Address;
400    GpeBlock->SpaceId = SpaceId;
401    GpeBlock->Node = GpeDevice;
402    GpeBlock->GpeCount = (UINT16) (RegisterCount * ACPI_GPE_REGISTER_WIDTH);
403    GpeBlock->Initialized = FALSE;
404    GpeBlock->RegisterCount = RegisterCount;
405    GpeBlock->BlockBaseNumber = GpeBlockBaseNumber;
406
407    /*
408     * Create the RegisterInfo and EventInfo sub-structures
409     * Note: disables and clears all GPEs in the block
410     */
411    Status = AcpiEvCreateGpeInfoBlocks (GpeBlock);
412    if (ACPI_FAILURE (Status))
413    {
414        ACPI_FREE (GpeBlock);
415        return_ACPI_STATUS (Status);
416    }
417
418    /* Install the new block in the global lists */
419
420    Status = AcpiEvInstallGpeBlock (GpeBlock, InterruptNumber);
421    if (ACPI_FAILURE (Status))
422    {
423        ACPI_FREE (GpeBlock->RegisterInfo);
424        ACPI_FREE (GpeBlock->EventInfo);
425        ACPI_FREE (GpeBlock);
426        return_ACPI_STATUS (Status);
427    }
428
429    AcpiGbl_AllGpesInitialized = FALSE;
430
431    /* Find all GPE methods (_Lxx or_Exx) for this block */
432
433    WalkInfo.GpeBlock = GpeBlock;
434    WalkInfo.GpeDevice = GpeDevice;
435    WalkInfo.ExecuteByOwnerId = FALSE;
436
437    Status = AcpiNsWalkNamespace (ACPI_TYPE_METHOD, GpeDevice,
438        ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
439        AcpiEvMatchGpeMethod, NULL, &WalkInfo, NULL);
440
441    /* Return the new block */
442
443    if (ReturnGpeBlock)
444    {
445        (*ReturnGpeBlock) = GpeBlock;
446    }
447
448    ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT,
449        "    Initialized GPE %02X to %02X [%4.4s] %u regs on interrupt 0x%X%s\n",
450        (UINT32) GpeBlock->BlockBaseNumber,
451        (UINT32) (GpeBlock->BlockBaseNumber + (GpeBlock->GpeCount - 1)),
452        GpeDevice->Name.Ascii, GpeBlock->RegisterCount, InterruptNumber,
453        InterruptNumber == AcpiGbl_FADT.SciInterrupt ? " (SCI)" : ""));
454
455    /* Update global count of currently available GPEs */
456
457    AcpiCurrentGpeCount += GpeBlock->GpeCount;
458    return_ACPI_STATUS (AE_OK);
459}
460
461
462/*******************************************************************************
463 *
464 * FUNCTION:    AcpiEvInitializeGpeBlock
465 *
466 * PARAMETERS:  ACPI_GPE_CALLBACK
467 *
468 * RETURN:      Status
469 *
470 * DESCRIPTION: Initialize and enable a GPE block. Enable GPEs that have
471 *              associated methods.
472 *              Note: Assumes namespace is locked.
473 *
474 ******************************************************************************/
475
476ACPI_STATUS
477AcpiEvInitializeGpeBlock (
478    ACPI_GPE_XRUPT_INFO     *GpeXruptInfo,
479    ACPI_GPE_BLOCK_INFO     *GpeBlock,
480    void                    *Ignored)
481{
482    ACPI_STATUS             Status;
483    ACPI_GPE_EVENT_INFO     *GpeEventInfo;
484    UINT32                  GpeEnabledCount;
485    UINT32                  GpeIndex;
486    UINT32                  i;
487    UINT32                  j;
488
489
490    ACPI_FUNCTION_TRACE (EvInitializeGpeBlock);
491
492
493    /*
494     * Ignore a null GPE block (e.g., if no GPE block 1 exists), and
495     * any GPE blocks that have been initialized already.
496     */
497    if (!GpeBlock || GpeBlock->Initialized)
498    {
499        return_ACPI_STATUS (AE_OK);
500    }
501
502    /*
503     * Enable all GPEs that have a corresponding method and have the
504     * ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block
505     * must be enabled via the acpi_enable_gpe() interface.
506     */
507    GpeEnabledCount = 0;
508
509    for (i = 0; i < GpeBlock->RegisterCount; i++)
510    {
511        for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++)
512        {
513            /* Get the info block for this particular GPE */
514
515            GpeIndex = (i * ACPI_GPE_REGISTER_WIDTH) + j;
516            GpeEventInfo = &GpeBlock->EventInfo[GpeIndex];
517
518            /*
519             * Ignore GPEs that have no corresponding _Lxx/_Exx method
520             * and GPEs that are used to wake the system
521             */
522            if ((ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_NONE) ||
523                (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_HANDLER) ||
524                (ACPI_GPE_DISPATCH_TYPE (GpeEventInfo->Flags) == ACPI_GPE_DISPATCH_RAW_HANDLER) ||
525                (GpeEventInfo->Flags & ACPI_GPE_CAN_WAKE))
526            {
527                continue;
528            }
529
530            Status = AcpiEvAddGpeReference (GpeEventInfo);
531            if (ACPI_FAILURE (Status))
532            {
533                ACPI_EXCEPTION ((AE_INFO, Status,
534                    "Could not enable GPE 0x%02X",
535                    GpeIndex + GpeBlock->BlockBaseNumber));
536                continue;
537            }
538
539            GpeEnabledCount++;
540        }
541    }
542
543    if (GpeEnabledCount)
544    {
545        ACPI_INFO ((
546            "Enabled %u GPEs in block %02X to %02X", GpeEnabledCount,
547            (UINT32) GpeBlock->BlockBaseNumber,
548            (UINT32) (GpeBlock->BlockBaseNumber + (GpeBlock->GpeCount - 1))));
549    }
550
551    GpeBlock->Initialized = TRUE;
552    return_ACPI_STATUS (AE_OK);
553}
554
555#endif /* !ACPI_REDUCED_HARDWARE */
556