/******************************************************************************1*2* Module Name: evgpeblk - GPE block creation and initialization.3*4*****************************************************************************/56/*7* Copyright (C) 2000 - 2011, Intel Corp.8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions, and the following disclaimer,15* without modification.16* 2. Redistributions in binary form must reproduce at minimum a disclaimer17* substantially similar to the "NO WARRANTY" disclaimer below18* ("Disclaimer") and any redistribution must be conditioned upon19* including a substantially similar Disclaimer requirement for further20* binary redistribution.21* 3. Neither the names of the above-listed copyright holders nor the names22* of any contributors may be used to endorse or promote products derived23* from this software without specific prior written permission.24*25* Alternatively, this software may be distributed under the terms of the26* GNU General Public License ("GPL") version 2 as published by the Free27* Software Foundation.28*29* NO WARRANTY30* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS31* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT32* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR33* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT34* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL35* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS36* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)37* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,38* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING39* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE40* POSSIBILITY OF SUCH DAMAGES.41*/4243#include <acpi/acpi.h>44#include "accommon.h"45#include "acevents.h"46#include "acnamesp.h"4748#define _COMPONENT ACPI_EVENTS49ACPI_MODULE_NAME("evgpeblk")5051/* Local prototypes */52static acpi_status53acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,54u32 interrupt_number);5556static acpi_status57acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block);5859/*******************************************************************************60*61* FUNCTION: acpi_ev_install_gpe_block62*63* PARAMETERS: gpe_block - New GPE block64* interrupt_number - Xrupt to be associated with this65* GPE block66*67* RETURN: Status68*69* DESCRIPTION: Install new GPE block with mutex support70*71******************************************************************************/7273static acpi_status74acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block,75u32 interrupt_number)76{77struct acpi_gpe_block_info *next_gpe_block;78struct acpi_gpe_xrupt_info *gpe_xrupt_block;79acpi_status status;80acpi_cpu_flags flags;8182ACPI_FUNCTION_TRACE(ev_install_gpe_block);8384status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);85if (ACPI_FAILURE(status)) {86return_ACPI_STATUS(status);87}8889gpe_xrupt_block = acpi_ev_get_gpe_xrupt_block(interrupt_number);90if (!gpe_xrupt_block) {91status = AE_NO_MEMORY;92goto unlock_and_exit;93}9495/* Install the new block at the end of the list with lock */9697flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);98if (gpe_xrupt_block->gpe_block_list_head) {99next_gpe_block = gpe_xrupt_block->gpe_block_list_head;100while (next_gpe_block->next) {101next_gpe_block = next_gpe_block->next;102}103104next_gpe_block->next = gpe_block;105gpe_block->previous = next_gpe_block;106} else {107gpe_xrupt_block->gpe_block_list_head = gpe_block;108}109110gpe_block->xrupt_block = gpe_xrupt_block;111acpi_os_release_lock(acpi_gbl_gpe_lock, flags);112113unlock_and_exit:114status = acpi_ut_release_mutex(ACPI_MTX_EVENTS);115return_ACPI_STATUS(status);116}117118/*******************************************************************************119*120* FUNCTION: acpi_ev_delete_gpe_block121*122* PARAMETERS: gpe_block - Existing GPE block123*124* RETURN: Status125*126* DESCRIPTION: Remove a GPE block127*128******************************************************************************/129130acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block)131{132acpi_status status;133acpi_cpu_flags flags;134135ACPI_FUNCTION_TRACE(ev_install_gpe_block);136137status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);138if (ACPI_FAILURE(status)) {139return_ACPI_STATUS(status);140}141142/* Disable all GPEs in this block */143144status =145acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block, NULL);146147if (!gpe_block->previous && !gpe_block->next) {148149/* This is the last gpe_block on this interrupt */150151status = acpi_ev_delete_gpe_xrupt(gpe_block->xrupt_block);152if (ACPI_FAILURE(status)) {153goto unlock_and_exit;154}155} else {156/* Remove the block on this interrupt with lock */157158flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);159if (gpe_block->previous) {160gpe_block->previous->next = gpe_block->next;161} else {162gpe_block->xrupt_block->gpe_block_list_head =163gpe_block->next;164}165166if (gpe_block->next) {167gpe_block->next->previous = gpe_block->previous;168}169acpi_os_release_lock(acpi_gbl_gpe_lock, flags);170}171172acpi_current_gpe_count -= gpe_block->gpe_count;173174/* Free the gpe_block */175176ACPI_FREE(gpe_block->register_info);177ACPI_FREE(gpe_block->event_info);178ACPI_FREE(gpe_block);179180unlock_and_exit:181status = acpi_ut_release_mutex(ACPI_MTX_EVENTS);182return_ACPI_STATUS(status);183}184185/*******************************************************************************186*187* FUNCTION: acpi_ev_create_gpe_info_blocks188*189* PARAMETERS: gpe_block - New GPE block190*191* RETURN: Status192*193* DESCRIPTION: Create the register_info and event_info blocks for this GPE block194*195******************************************************************************/196197static acpi_status198acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block)199{200struct acpi_gpe_register_info *gpe_register_info = NULL;201struct acpi_gpe_event_info *gpe_event_info = NULL;202struct acpi_gpe_event_info *this_event;203struct acpi_gpe_register_info *this_register;204u32 i;205u32 j;206acpi_status status;207208ACPI_FUNCTION_TRACE(ev_create_gpe_info_blocks);209210/* Allocate the GPE register information block */211212gpe_register_info = ACPI_ALLOCATE_ZEROED((acpi_size) gpe_block->213register_count *214sizeof(struct215acpi_gpe_register_info));216if (!gpe_register_info) {217ACPI_ERROR((AE_INFO,218"Could not allocate the GpeRegisterInfo table"));219return_ACPI_STATUS(AE_NO_MEMORY);220}221222/*223* Allocate the GPE event_info block. There are eight distinct GPEs224* per register. Initialization to zeros is sufficient.225*/226gpe_event_info = ACPI_ALLOCATE_ZEROED((acpi_size) gpe_block->gpe_count *227sizeof(struct228acpi_gpe_event_info));229if (!gpe_event_info) {230ACPI_ERROR((AE_INFO,231"Could not allocate the GpeEventInfo table"));232status = AE_NO_MEMORY;233goto error_exit;234}235236/* Save the new Info arrays in the GPE block */237238gpe_block->register_info = gpe_register_info;239gpe_block->event_info = gpe_event_info;240241/*242* Initialize the GPE Register and Event structures. A goal of these243* tables is to hide the fact that there are two separate GPE register244* sets in a given GPE hardware block, the status registers occupy the245* first half, and the enable registers occupy the second half.246*/247this_register = gpe_register_info;248this_event = gpe_event_info;249250for (i = 0; i < gpe_block->register_count; i++) {251252/* Init the register_info for this GPE register (8 GPEs) */253254this_register->base_gpe_number =255(u8) (gpe_block->block_base_number +256(i * ACPI_GPE_REGISTER_WIDTH));257258this_register->status_address.address =259gpe_block->block_address.address + i;260261this_register->enable_address.address =262gpe_block->block_address.address + i +263gpe_block->register_count;264265this_register->status_address.space_id =266gpe_block->block_address.space_id;267this_register->enable_address.space_id =268gpe_block->block_address.space_id;269this_register->status_address.bit_width =270ACPI_GPE_REGISTER_WIDTH;271this_register->enable_address.bit_width =272ACPI_GPE_REGISTER_WIDTH;273this_register->status_address.bit_offset = 0;274this_register->enable_address.bit_offset = 0;275276/* Init the event_info for each GPE within this register */277278for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {279this_event->gpe_number =280(u8) (this_register->base_gpe_number + j);281this_event->register_info = this_register;282this_event++;283}284285/* Disable all GPEs within this register */286287status = acpi_hw_write(0x00, &this_register->enable_address);288if (ACPI_FAILURE(status)) {289goto error_exit;290}291292/* Clear any pending GPE events within this register */293294status = acpi_hw_write(0xFF, &this_register->status_address);295if (ACPI_FAILURE(status)) {296goto error_exit;297}298299this_register++;300}301302return_ACPI_STATUS(AE_OK);303304error_exit:305if (gpe_register_info) {306ACPI_FREE(gpe_register_info);307}308if (gpe_event_info) {309ACPI_FREE(gpe_event_info);310}311312return_ACPI_STATUS(status);313}314315/*******************************************************************************316*317* FUNCTION: acpi_ev_create_gpe_block318*319* PARAMETERS: gpe_device - Handle to the parent GPE block320* gpe_block_address - Address and space_iD321* register_count - Number of GPE register pairs in the block322* gpe_block_base_number - Starting GPE number for the block323* interrupt_number - H/W interrupt for the block324* return_gpe_block - Where the new block descriptor is returned325*326* RETURN: Status327*328* DESCRIPTION: Create and Install a block of GPE registers. All GPEs within329* the block are disabled at exit.330* Note: Assumes namespace is locked.331*332******************************************************************************/333334acpi_status335acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,336struct acpi_generic_address *gpe_block_address,337u32 register_count,338u8 gpe_block_base_number,339u32 interrupt_number,340struct acpi_gpe_block_info **return_gpe_block)341{342acpi_status status;343struct acpi_gpe_block_info *gpe_block;344struct acpi_gpe_walk_info walk_info;345346ACPI_FUNCTION_TRACE(ev_create_gpe_block);347348if (!register_count) {349return_ACPI_STATUS(AE_OK);350}351352/* Allocate a new GPE block */353354gpe_block = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_block_info));355if (!gpe_block) {356return_ACPI_STATUS(AE_NO_MEMORY);357}358359/* Initialize the new GPE block */360361gpe_block->node = gpe_device;362gpe_block->gpe_count = (u16)(register_count * ACPI_GPE_REGISTER_WIDTH);363gpe_block->initialized = FALSE;364gpe_block->register_count = register_count;365gpe_block->block_base_number = gpe_block_base_number;366367ACPI_MEMCPY(&gpe_block->block_address, gpe_block_address,368sizeof(struct acpi_generic_address));369370/*371* Create the register_info and event_info sub-structures372* Note: disables and clears all GPEs in the block373*/374status = acpi_ev_create_gpe_info_blocks(gpe_block);375if (ACPI_FAILURE(status)) {376ACPI_FREE(gpe_block);377return_ACPI_STATUS(status);378}379380/* Install the new block in the global lists */381382status = acpi_ev_install_gpe_block(gpe_block, interrupt_number);383if (ACPI_FAILURE(status)) {384ACPI_FREE(gpe_block);385return_ACPI_STATUS(status);386}387388acpi_gbl_all_gpes_initialized = FALSE;389390/* Find all GPE methods (_Lxx or_Exx) for this block */391392walk_info.gpe_block = gpe_block;393walk_info.gpe_device = gpe_device;394walk_info.execute_by_owner_id = FALSE;395396status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device,397ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,398acpi_ev_match_gpe_method, NULL,399&walk_info, NULL);400401/* Return the new block */402403if (return_gpe_block) {404(*return_gpe_block) = gpe_block;405}406407ACPI_DEBUG_PRINT((ACPI_DB_INIT,408"GPE %02X to %02X [%4.4s] %u regs on int 0x%X\n",409(u32) gpe_block->block_base_number,410(u32) (gpe_block->block_base_number +411(gpe_block->gpe_count - 1)),412gpe_device->name.ascii, gpe_block->register_count,413interrupt_number));414415/* Update global count of currently available GPEs */416417acpi_current_gpe_count += gpe_block->gpe_count;418return_ACPI_STATUS(AE_OK);419}420421/*******************************************************************************422*423* FUNCTION: acpi_ev_initialize_gpe_block424*425* PARAMETERS: acpi_gpe_callback426*427* RETURN: Status428*429* DESCRIPTION: Initialize and enable a GPE block. Enable GPEs that have430* associated methods.431* Note: Assumes namespace is locked.432*433******************************************************************************/434435acpi_status436acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,437struct acpi_gpe_block_info *gpe_block,438void *ignored)439{440acpi_status status;441struct acpi_gpe_event_info *gpe_event_info;442u32 gpe_enabled_count;443u32 gpe_index;444u32 i;445u32 j;446447ACPI_FUNCTION_TRACE(ev_initialize_gpe_block);448449/*450* Ignore a null GPE block (e.g., if no GPE block 1 exists), and451* any GPE blocks that have been initialized already.452*/453if (!gpe_block || gpe_block->initialized) {454return_ACPI_STATUS(AE_OK);455}456457/*458* Enable all GPEs that have a corresponding method and have the459* ACPI_GPE_CAN_WAKE flag unset. Any other GPEs within this block460* must be enabled via the acpi_enable_gpe() interface.461*/462gpe_enabled_count = 0;463464for (i = 0; i < gpe_block->register_count; i++) {465for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {466467/* Get the info block for this particular GPE */468469gpe_index = (i * ACPI_GPE_REGISTER_WIDTH) + j;470gpe_event_info = &gpe_block->event_info[gpe_index];471472/*473* Ignore GPEs that have no corresponding _Lxx/_Exx method474* and GPEs that are used to wake the system475*/476if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==477ACPI_GPE_DISPATCH_NONE)478|| ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)479== ACPI_GPE_DISPATCH_HANDLER)480|| (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {481continue;482}483484status = acpi_ev_add_gpe_reference(gpe_event_info);485if (ACPI_FAILURE(status)) {486ACPI_EXCEPTION((AE_INFO, status,487"Could not enable GPE 0x%02X",488gpe_index + gpe_block->block_base_number));489continue;490}491492gpe_enabled_count++;493}494}495496if (gpe_enabled_count) {497ACPI_DEBUG_PRINT((ACPI_DB_INIT,498"Enabled %u GPEs in this block\n",499gpe_enabled_count));500}501502gpe_block->initialized = TRUE;503504return_ACPI_STATUS(AE_OK);505}506507508