/****************************************************************************** * * Module Name: evevent - Fixed and General Purpose Acpi_event * handling and dispatch * $Revision: 1.1.1.1 $ * *****************************************************************************/ /* * Copyright (C) 2000, 2001 R. Byron Moore * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "acpi.h" #include "achware.h" #include "acevents.h" #include "acnamesp.h" #define _COMPONENT ACPI_EVENTS MODULE_NAME ("evevent") /******************************************************************************* * * FUNCTION: Acpi_ev_initialize * * PARAMETERS: None * * RETURN: Status * * DESCRIPTION: Ensures that the system control interrupt (SCI) is properly * configured, disables SCI event sources, installs the SCI * handler * ******************************************************************************/ acpi_status acpi_ev_initialize ( void) { acpi_status status; FUNCTION_TRACE ("Ev_initialize"); /* Make sure we have ACPI tables */ if (!acpi_gbl_DSDT) { ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No ACPI tables present!\n")); return_ACPI_STATUS (AE_NO_ACPI_TABLES); } /* Make sure the BIOS supports ACPI mode */ if (SYS_MODE_LEGACY == acpi_hw_get_mode_capabilities()) { ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "ACPI Mode is not supported!\n")); return_ACPI_STATUS (AE_ERROR); } acpi_gbl_original_mode = acpi_hw_get_mode(); /* * Initialize the Fixed and General Purpose Acpi_events prior. This is * done prior to enabling SCIs to prevent interrupts from occuring * before handers are installed. */ status = acpi_ev_fixed_event_initialize (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_FATAL, "Unable to initialize fixed events.\n")); return_ACPI_STATUS (status); } status = acpi_ev_gpe_initialize (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_FATAL, "Unable to initialize general purpose events.\n")); return_ACPI_STATUS (status); } /* Install the SCI handler */ status = acpi_ev_install_sci_handler (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_FATAL, "Unable to install System Control Interrupt Handler\n")); return_ACPI_STATUS (status); } /* Install handlers for control method GPE handlers (_Lxx, _Exx) */ status = acpi_ev_init_gpe_control_methods (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_FATAL, "Unable to initialize Gpe control methods\n")); return_ACPI_STATUS (status); } /* Install the handler for the Global Lock */ status = acpi_ev_init_global_lock_handler (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_FATAL, "Unable to initialize Global Lock handler\n")); return_ACPI_STATUS (status); } return_ACPI_STATUS (status); } /******************************************************************************* * * FUNCTION: Acpi_ev_fixed_event_initialize * * PARAMETERS: None * * RETURN: Status * * DESCRIPTION: Initialize the Fixed Acpi_event data structures * ******************************************************************************/ acpi_status acpi_ev_fixed_event_initialize(void) { int i = 0; /* Initialize the structure that keeps track of fixed event handlers */ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { acpi_gbl_fixed_event_handlers[i].handler = NULL; acpi_gbl_fixed_event_handlers[i].context = NULL; } acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, TMR_EN, 0); acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, GBL_EN, 0); acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, PWRBTN_EN, 0); acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, SLPBTN_EN, 0); acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, RTC_EN, 0); return (AE_OK); } /******************************************************************************* * * FUNCTION: Acpi_ev_fixed_event_detect * * PARAMETERS: None * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * * DESCRIPTION: Checks the PM status register for fixed events * ******************************************************************************/ u32 acpi_ev_fixed_event_detect (void) { u32 int_status = INTERRUPT_NOT_HANDLED; u32 status_register; u32 enable_register; PROC_NAME ("Ev_fixed_event_detect"); /* * Read the fixed feature status and enable registers, as all the cases * depend on their values. */ status_register = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, PM1_STS); enable_register = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, PM1_EN); ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS, "Fixed Acpi_event Block: Enable %08X Status %08X\n", enable_register, status_register)); /* power management timer roll over */ if ((status_register & ACPI_STATUS_PMTIMER) && (enable_register & ACPI_ENABLE_PMTIMER)) { int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_PMTIMER); } /* global event (BIOS wants the global lock) */ if ((status_register & ACPI_STATUS_GLOBAL) && (enable_register & ACPI_ENABLE_GLOBAL)) { int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_GLOBAL); } /* power button event */ if ((status_register & ACPI_STATUS_POWER_BUTTON) && (enable_register & ACPI_ENABLE_POWER_BUTTON)) { int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_POWER_BUTTON); } /* sleep button event */ if ((status_register & ACPI_STATUS_SLEEP_BUTTON) && (enable_register & ACPI_ENABLE_SLEEP_BUTTON)) { int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_SLEEP_BUTTON); } return (int_status); } /******************************************************************************* * * FUNCTION: Acpi_ev_fixed_event_dispatch * * PARAMETERS: Event - Event type * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * * DESCRIPTION: Clears the status bit for the requested event, calls the * handler that previously registered for the event. * ******************************************************************************/ u32 acpi_ev_fixed_event_dispatch ( u32 event) { u32 register_id; FUNCTION_ENTRY (); /* Clear the status bit */ switch (event) { case ACPI_EVENT_PMTIMER: register_id = TMR_STS; break; case ACPI_EVENT_GLOBAL: register_id = GBL_STS; break; case ACPI_EVENT_POWER_BUTTON: register_id = PWRBTN_STS; break; case ACPI_EVENT_SLEEP_BUTTON: register_id = SLPBTN_STS; break; case ACPI_EVENT_RTC: register_id = RTC_STS; break; default: return 0; break; } acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, register_id, 1); /* * Make sure we've got a handler. If not, report an error. * The event is disabled to prevent further interrupts. */ if (NULL == acpi_gbl_fixed_event_handlers[event].handler) { register_id = (PM1_EN | REGISTER_BIT_ID(register_id)); acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, register_id, 0); REPORT_ERROR ( ("Ev_gpe_dispatch: No installed handler for fixed event [%08X]\n", event)); return (INTERRUPT_NOT_HANDLED); } /* Invoke the handler */ return ((acpi_gbl_fixed_event_handlers[event].handler)( acpi_gbl_fixed_event_handlers[event].context)); } /******************************************************************************* * * FUNCTION: Acpi_ev_gpe_initialize * * PARAMETERS: None * * RETURN: Status * * DESCRIPTION: Initialize the GPE data structures * ******************************************************************************/ acpi_status acpi_ev_gpe_initialize (void) { u32 i; u32 j; u32 register_index; u32 gpe_number; u16 gpe0register_count; u16 gpe1_register_count; FUNCTION_TRACE ("Ev_gpe_initialize"); /* * Set up various GPE counts * * You may ask,why are the GPE register block lengths divided by 2? * From the ACPI 2.0 Spec, section, 4.7.1.6 General-Purpose Event * Registers, we have, * * "Each register block contains two registers of equal length * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN * The length of the GPE1_STS and GPE1_EN registers is equal to * half the GPE1_LEN. If a generic register block is not supported * then its respective block pointer and block length values in the * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need * to be the same size." */ gpe0register_count = (u16) DIV_2 (acpi_gbl_FADT->gpe0blk_len); gpe1_register_count = (u16) DIV_2 (acpi_gbl_FADT->gpe1_blk_len); acpi_gbl_gpe_register_count = gpe0register_count + gpe1_register_count; if (!acpi_gbl_gpe_register_count) { REPORT_WARNING (("Zero GPEs are defined in the FADT\n")); return_ACPI_STATUS (AE_OK); } /* * Allocate the Gpe information block */ acpi_gbl_gpe_registers = ACPI_MEM_CALLOCATE (acpi_gbl_gpe_register_count * sizeof (acpi_gpe_registers)); if (!acpi_gbl_gpe_registers) { ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not allocate the Gpe_registers block\n")); return_ACPI_STATUS (AE_NO_MEMORY); } /* * Allocate the Gpe dispatch handler block * There are eight distinct GP events per register. * Initialization to zeros is sufficient */ acpi_gbl_gpe_info = ACPI_MEM_CALLOCATE (MUL_8 (acpi_gbl_gpe_register_count) * sizeof (acpi_gpe_level_info)); if (!acpi_gbl_gpe_info) { ACPI_MEM_FREE (acpi_gbl_gpe_registers); ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not allocate the Gpe_info block\n")); return_ACPI_STATUS (AE_NO_MEMORY); } /* Set the Gpe validation table to GPE_INVALID */ MEMSET (acpi_gbl_gpe_valid, (int) ACPI_GPE_INVALID, ACPI_NUM_GPE); /* * Initialize the Gpe information and validation blocks. A goal of these * blocks is to hide the fact that there are two separate GPE register sets * In a given block, the status registers occupy the first half, and * the enable registers occupy the second half. */ /* GPE Block 0 */ register_index = 0; for (i = 0; i < gpe0register_count; i++) { acpi_gbl_gpe_registers[register_index].status_addr = (u16) (ACPI_GET_ADDRESS (acpi_gbl_FADT->Xgpe0blk.address) + i); acpi_gbl_gpe_registers[register_index].enable_addr = (u16) (ACPI_GET_ADDRESS (acpi_gbl_FADT->Xgpe0blk.address) + i + gpe0register_count); acpi_gbl_gpe_registers[register_index].gpe_base = (u8) MUL_8 (i); for (j = 0; j < 8; j++) { gpe_number = acpi_gbl_gpe_registers[register_index].gpe_base + j; acpi_gbl_gpe_valid[gpe_number] = (u8) register_index; } /* * Clear the status/enable registers. Note that status registers * are cleared by writing a '1', while enable registers are cleared * by writing a '0'. */ acpi_os_write_port (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00, 8); acpi_os_write_port (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF, 8); register_index++; } /* GPE Block 1 */ for (i = 0; i < gpe1_register_count; i++) { acpi_gbl_gpe_registers[register_index].status_addr = (u16) (ACPI_GET_ADDRESS (acpi_gbl_FADT->Xgpe1_blk.address) + i); acpi_gbl_gpe_registers[register_index].enable_addr = (u16) (ACPI_GET_ADDRESS (acpi_gbl_FADT->Xgpe1_blk.address) + i + gpe1_register_count); acpi_gbl_gpe_registers[register_index].gpe_base = (u8) (acpi_gbl_FADT->gpe1_base + MUL_8 (i)); for (j = 0; j < 8; j++) { gpe_number = acpi_gbl_gpe_registers[register_index].gpe_base + j; acpi_gbl_gpe_valid[gpe_number] = (u8) register_index; } /* * Clear the status/enable registers. Note that status registers * are cleared by writing a '1', while enable registers are cleared * by writing a '0'. */ acpi_os_write_port (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00, 8); acpi_os_write_port (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF, 8); register_index++; } ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "GPE registers: %X@%8.8X%8.8X (Blk0) %X@%8.8X%8.8X (Blk1)\n", gpe0register_count, HIDWORD(acpi_gbl_FADT->Xgpe0blk.address), LODWORD(acpi_gbl_FADT->Xgpe0blk.address), gpe1_register_count, HIDWORD(acpi_gbl_FADT->Xgpe1_blk.address), LODWORD(acpi_gbl_FADT->Xgpe1_blk.address))); return_ACPI_STATUS (AE_OK); } /******************************************************************************* * * FUNCTION: Acpi_ev_save_method_info * * PARAMETERS: None * * RETURN: None * * DESCRIPTION: Called from Acpi_walk_namespace. Expects each object to be a * control method under the _GPE portion of the namespace. * Extract the name and GPE type from the object, saving this * information for quick lookup during GPE dispatch * * The name of each GPE control method is of the form: * "_Lnn" or "_Enn" * Where: * L - means that the GPE is level triggered * E - means that the GPE is edge triggered * nn - is the GPE number * ******************************************************************************/ static acpi_status acpi_ev_save_method_info ( acpi_handle obj_handle, u32 level, void *obj_desc, void **return_value) { u32 gpe_number; NATIVE_CHAR name[ACPI_NAME_SIZE + 1]; u8 type; PROC_NAME ("Ev_save_method_info"); /* Extract the name from the object and convert to a string */ MOVE_UNALIGNED32_TO_32 (name, &((acpi_namespace_node *) obj_handle)->name); name[ACPI_NAME_SIZE] = 0; /* * Edge/Level determination is based on the 2nd s8 of the method name */ if (name[1] == 'L') { type = ACPI_EVENT_LEVEL_TRIGGERED; } else if (name[1] == 'E') { type = ACPI_EVENT_EDGE_TRIGGERED; } else { /* Unknown method type, just ignore it! */ ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown GPE method type: %s (name not of form _Lnn or _Enn)\n", name)); return (AE_OK); } /* Convert the last two characters of the name to the Gpe Number */ gpe_number = STRTOUL (&name[2], NULL, 16); if (gpe_number == ACPI_UINT32_MAX) { /* Conversion failed; invalid method, just ignore it */ ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not extract GPE number from name: %s (name not of form _Lnn or _Enn)\n", name)); return (AE_OK); } /* Ensure that we have a valid GPE number */ if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { /* Not valid, all we can do here is ignore it */ return (AE_OK); } /* * Now we can add this information to the Gpe_info block * for use during dispatch of this GPE. */ acpi_gbl_gpe_info [gpe_number].type = type; acpi_gbl_gpe_info [gpe_number].method_handle = obj_handle; /* * Enable the GPE (SCIs should be disabled at this point) */ acpi_hw_enable_gpe (gpe_number); ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Registered GPE method %s as GPE number %X\n", name, gpe_number)); return (AE_OK); } /******************************************************************************* * * FUNCTION: Acpi_ev_init_gpe_control_methods * * PARAMETERS: None * * RETURN: None * * DESCRIPTION: Obtain the control methods associated with the GPEs. * * NOTE: Must be called AFTER namespace initialization! * ******************************************************************************/ acpi_status acpi_ev_init_gpe_control_methods (void) { acpi_status status; FUNCTION_TRACE ("Ev_init_gpe_control_methods"); /* Get a permanent handle to the _GPE object */ status = acpi_get_handle (NULL, "\\_GPE", &acpi_gbl_gpe_obj_handle); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } /* Traverse the namespace under \_GPE to find all methods there */ status = acpi_walk_namespace (ACPI_TYPE_METHOD, acpi_gbl_gpe_obj_handle, ACPI_UINT32_MAX, acpi_ev_save_method_info, NULL, NULL); return_ACPI_STATUS (status); } /******************************************************************************* * * FUNCTION: Acpi_ev_gpe_detect * * PARAMETERS: None * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * * DESCRIPTION: Detect if any GP events have occurred * ******************************************************************************/ u32 acpi_ev_gpe_detect (void) { u32 int_status = INTERRUPT_NOT_HANDLED; u32 i; u32 j; u8 enabled_status_byte; u8 bit_mask; PROC_NAME ("Ev_gpe_detect"); /* * Read all of the 8-bit GPE status and enable registers * in both of the register blocks, saving all of it. * Find all currently active GP events. */ for (i = 0; i < acpi_gbl_gpe_register_count; i++) { acpi_os_read_port (acpi_gbl_gpe_registers[i].status_addr, &acpi_gbl_gpe_registers[i].status, 8); acpi_os_read_port (acpi_gbl_gpe_registers[i].enable_addr, &acpi_gbl_gpe_registers[i].enable, 8); ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS, "GPE block at %X - Enable %08X Status %08X\n", acpi_gbl_gpe_registers[i].enable_addr, acpi_gbl_gpe_registers[i].status, acpi_gbl_gpe_registers[i].enable)); /* First check if there is anything active at all in this register */ enabled_status_byte = (u8) (acpi_gbl_gpe_registers[i].status & acpi_gbl_gpe_registers[i].enable); if (!enabled_status_byte) { /* No active GPEs in this register, move on */ continue; } /* Now look at the individual GPEs in this byte register */ for (j = 0, bit_mask = 1; j < 8; j++, bit_mask <<= 1) { /* Examine one GPE bit */ if (enabled_status_byte & bit_mask) { /* * Found an active GPE. Dispatch the event to a handler * or method. */ int_status |= acpi_ev_gpe_dispatch ( acpi_gbl_gpe_registers[i].gpe_base + j); } } } return (int_status); } /******************************************************************************* * * FUNCTION: Acpi_ev_asynch_execute_gpe_method * * PARAMETERS: Gpe_number - The 0-based Gpe number * * RETURN: None * * DESCRIPTION: Perform the actual execution of a GPE control method. This * function is called from an invocation of Acpi_os_queue_for_execution * (and therefore does NOT execute at interrupt level) so that * the control method itself is not executed in the context of * the SCI interrupt handler. * ******************************************************************************/ static void acpi_ev_asynch_execute_gpe_method ( void *context) { u32 gpe_number = (u32) context; acpi_gpe_level_info gpe_info; FUNCTION_TRACE ("Ev_asynch_execute_gpe_method"); /* * Take a snapshot of the GPE info for this level */ acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); gpe_info = acpi_gbl_gpe_info [gpe_number]; acpi_ut_release_mutex (ACPI_MTX_EVENTS); /* * Method Handler (_Lxx, _Exx): * ---------------------------- * Evaluate the _Lxx/_Exx control method that corresponds to this GPE. */ if (gpe_info.method_handle) { acpi_ns_evaluate_by_handle (gpe_info.method_handle, NULL, NULL); } /* * Level-Triggered? * ---------------- * If level-triggered we clear the GPE status bit after handling the event. */ if (gpe_info.type & ACPI_EVENT_LEVEL_TRIGGERED) { acpi_hw_clear_gpe (gpe_number); } /* * Enable the GPE. */ acpi_hw_enable_gpe (gpe_number); return_VOID; } /******************************************************************************* * * FUNCTION: Acpi_ev_gpe_dispatch * * PARAMETERS: Gpe_number - The 0-based Gpe number * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * * DESCRIPTION: Handle and dispatch a General Purpose Acpi_event. * Clears the status bit for the requested event. * * TBD: [Investigate] is this still valid or necessary: * The Gpe handler differs from the fixed events in that it clears the enable * bit rather than the status bit to clear the interrupt. This allows * software outside of interrupt context to determine what caused the SCI and * dispatch the correct AML. * ******************************************************************************/ u32 acpi_ev_gpe_dispatch ( u32 gpe_number) { acpi_gpe_level_info gpe_info; FUNCTION_TRACE ("Ev_gpe_dispatch"); /* * Valid GPE number? */ if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid GPE bit [%X].\n", gpe_number)); return_VALUE (INTERRUPT_NOT_HANDLED); } /* * Disable the GPE. */ acpi_hw_disable_gpe (gpe_number); gpe_info = acpi_gbl_gpe_info [gpe_number]; /* * Edge-Triggered? * --------------- * If edge-triggered, clear the GPE status bit now. Note that * level-triggered events are cleared after the GPE is serviced. */ if (gpe_info.type & ACPI_EVENT_EDGE_TRIGGERED) { acpi_hw_clear_gpe (gpe_number); } /* * Function Handler (e.g. EC)? */ if (gpe_info.handler) { /* Invoke function handler (at interrupt level). */ gpe_info.handler (gpe_info.context); /* Level-Triggered? */ if (gpe_info.type & ACPI_EVENT_LEVEL_TRIGGERED) { acpi_hw_clear_gpe (gpe_number); } /* Enable GPE */ acpi_hw_enable_gpe (gpe_number); } /* * Method Handler (e.g. _Exx/_Lxx)? */ else if (gpe_info.method_handle) { if (ACPI_FAILURE(acpi_os_queue_for_execution (OSD_PRIORITY_GPE, acpi_ev_asynch_execute_gpe_method, (void*) gpe_number))) { /* * Shoudn't occur, but if it does report an error. Note that * the GPE will remain disabled until the ACPI Core Subsystem * is restarted, or the handler is removed/reinstalled. */ REPORT_ERROR (("Acpi_ev_gpe_dispatch: Unable to queue handler for GPE bit [%X]\n", gpe_number)); } } /* * No Handler? Report an error and leave the GPE disabled. */ else { REPORT_ERROR (("Acpi_ev_gpe_dispatch: No installed handler for GPE [%X]\n", gpe_number)); /* Level-Triggered? */ if (gpe_info.type & ACPI_EVENT_LEVEL_TRIGGERED) { acpi_hw_clear_gpe (gpe_number); } } return_VALUE (INTERRUPT_HANDLED); }