patch-2.4.0-test3 linux/drivers/acpi/events/evevent.c

Next file: linux/drivers/acpi/events/evmisc.c
Previous file: linux/drivers/acpi/dispatcher/dswstate.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test2/linux/drivers/acpi/events/evevent.c linux/drivers/acpi/events/evevent.c
@@ -0,0 +1,679 @@
+/******************************************************************************
+ *
+ * Module Name: evevent - Fixed and General Purpose Acpi_event
+ *                          handling and dispatch
+ *
+ *****************************************************************************/
+
+/*
+ *  Copyright (C) 2000 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 "hardware.h"
+#include "events.h"
+#include "namesp.h"
+#include "common.h"
+
+#define _COMPONENT          EVENT_HANDLING
+	 MODULE_NAME         ("evevent");
+
+
+/******************************************************************************
+ *
+ * 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 < NUM_FIXED_EVENTS; i++) {
+		acpi_gbl_fixed_event_handlers[i].handler = NULL;
+		acpi_gbl_fixed_event_handlers[i].context = NULL;
+	}
+
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_PMTIMER +
+			 TMR_EN, 0);
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_GLOBAL +
+			 TMR_EN, 0);
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_POWER_BUTTON +
+			 TMR_EN, 0);
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_SLEEP_BUTTON +
+			 TMR_EN, 0);
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_RTC +
+			 TMR_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 = 0;
+	u32                     enable_register = 0;
+
+	/*
+	 * Read the fixed feature status and enable registers, as all the cases
+	 * depend on their values.
+	 */
+
+	status_register = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk);
+	if (acpi_gbl_FACP->pm1b_evt_blk) {
+		status_register |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk);
+	}
+
+	enable_register = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk +
+			   DIV_2 (acpi_gbl_FACP->pm1_evt_len));
+	if (acpi_gbl_FACP->pm1b_evt_blk) {
+		enable_register |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk +
+				   DIV_2 (acpi_gbl_FACP->pm1_evt_len));
+	}
+
+	/* 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 want's 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)
+{
+	/* Clear the status bit */
+
+	acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, (s32)TMR_STS +
+			 event, 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) {
+		acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
+				 TMR_EN + event, 0);
+
+		REPORT_ERROR("No installed handler for fixed 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;
+
+
+	/*
+	 * Setup various GPE counts
+	 */
+
+	gpe0register_count      = (u16) DIV_2 (acpi_gbl_FACP->gpe0blk_len);
+	gpe1_register_count     = (u16) DIV_2 (acpi_gbl_FACP->gpe1_blk_len);
+	acpi_gbl_gpe_register_count = gpe0register_count + gpe1_register_count;
+
+	/*
+	 * Allocate the Gpe information block
+	 */
+
+	acpi_gbl_gpe_registers = acpi_cm_callocate (acpi_gbl_gpe_register_count *
+			 sizeof (ACPI_GPE_REGISTERS));
+	if (!acpi_gbl_gpe_registers) {
+		return (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_cm_callocate (MUL_8 (acpi_gbl_gpe_register_count) *
+			 sizeof (ACPI_GPE_LEVEL_INFO));
+	if (!acpi_gbl_gpe_info) {
+		acpi_cm_free (acpi_gbl_gpe_registers);
+		return (AE_NO_MEMORY);
+	}
+
+	/* Set the Gpe validation table to GPE_INVALID */
+
+	MEMSET (acpi_gbl_gpe_valid, (int) ACPI_GPE_INVALID, 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_gbl_FACP->gpe0blk + i);
+
+		acpi_gbl_gpe_registers[register_index].enable_addr =
+				 (u16) (acpi_gbl_FACP->gpe0blk + 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_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00);
+		acpi_os_out8 (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF);
+
+		register_index++;
+	}
+
+	/* GPE Block 1 */
+
+	for (i = 0; i < gpe1_register_count; i++) {
+		acpi_gbl_gpe_registers[register_index].status_addr =
+				 (u16) (acpi_gbl_FACP->gpe1_blk + i);
+
+		acpi_gbl_gpe_registers[register_index].enable_addr =
+				 (u16) (acpi_gbl_FACP->gpe1_blk + i + gpe1_register_count);
+
+		acpi_gbl_gpe_registers[register_index].gpe_base =
+				 (u8) (acpi_gbl_FACP->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_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00);
+		acpi_os_out8 (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF);
+
+		register_index++;
+	}
+
+	return (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
+ *
+ ******************************************************************************/
+
+ACPI_STATUS
+acpi_ev_save_method_info (
+	ACPI_HANDLE             obj_handle,
+	u32                     level,
+	void                    *obj_desc,
+	void                    **return_value)
+{
+	u32                     gpe_number;
+	char                    name[ACPI_NAME_SIZE + 1];
+	u8                      type;
+
+
+	/* Extract the name from the object and convert to a string */
+
+	MOVE_UNALIGNED32_TO_32 (name, &((ACPI_NAMED_OBJECT*) obj_handle)->name);
+	name[ACPI_NAME_SIZE] = 0;
+
+	/*
+	 * Edge/Level determination is based on the 2nd char 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! */
+
+		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 */
+
+		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);
+
+	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;
+
+
+	/* Get a permanent handle to the _GPE object */
+
+	status = acpi_get_handle (NULL, "\\_GPE", &acpi_gbl_gpe_obj_handle);
+	if (ACPI_FAILURE (status)) {
+		return (status);
+	}
+
+	/* Traverse the namespace under \_GPE to find all methods there */
+
+	status = acpi_walk_namespace (ACPI_TYPE_METHOD, acpi_gbl_gpe_obj_handle,
+			  ACPI_INT32_MAX, acpi_ev_save_method_info,
+			  NULL, NULL);
+
+	return (status);
+}
+
+
+/******************************************************************************
+ *
+ * FUNCTION:    Acpi_ev_gpe_cleanup
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Cleanup in preparation for unload.
+ *
+ ******************************************************************************/
+
+void
+acpi_ev_gpe_cleanup (void)
+{
+
+	acpi_cm_free (acpi_gbl_gpe_registers);
+	acpi_cm_free (acpi_gbl_gpe_info);
+
+	return;
+}
+
+
+/******************************************************************************
+ *
+ * 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;
+
+
+	/*
+	 * 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_gbl_gpe_registers[i].status =
+				   acpi_os_in8 (acpi_gbl_gpe_registers[i].status_addr);
+
+		acpi_gbl_gpe_registers[i].enable =
+				   acpi_os_in8 (acpi_gbl_gpe_registers[i].enable_addr);
+
+		/* 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.
+ *
+ ******************************************************************************/
+
+void
+acpi_ev_asynch_execute_gpe_method (
+	void                    *context)
+{
+	u32                     gpe_number = (u32) context;
+	ACPI_GPE_LEVEL_INFO     gpe_info;
+
+
+	/* Take a snapshot of the GPE info for this level */
+
+	acpi_cm_acquire_mutex (ACPI_MTX_EVENTS);
+	gpe_info = acpi_gbl_gpe_info [gpe_number];
+	acpi_cm_release_mutex (ACPI_MTX_EVENTS);
+
+	/*
+	 * Function Handler (e.g. EC):
+	 * ---------------------------
+	 * Execute the installed function handler to handle this event.
+	 */
+	if (gpe_info.handler) {
+		gpe_info.handler (gpe_info.context);
+	}
+
+	/*
+	 * Method Handler (_Lxx, _Exx):
+	 * ----------------------------
+	 * Acpi_evaluate the _Lxx/_Exx control method that corresponds to this GPE.
+	 */
+	else if (gpe_info.method_handle) {
+		acpi_ns_evaluate_by_handle (gpe_info.method_handle, NULL, NULL);
+	}
+
+	/*
+	 * Level-Triggered?
+	 * ----------------
+	 * If level-triggered, clear the GPE status bit after execution.  Note
+	 * that edge-triggered events are cleared prior to calling (via DPC)
+	 * this function.
+	 */
+	if (gpe_info.type | ACPI_EVENT_LEVEL_TRIGGERED) {
+		acpi_hw_clear_gpe (gpe_number);
+	}
+
+	/*
+	 * Enable the GPE.
+	 */
+	acpi_hw_enable_gpe (gpe_number);
+
+	return;
+}
+
+
+/******************************************************************************
+ *
+ * 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)
+{
+
+	/*DEBUG_INCREMENT_EVENT_COUNT (EVENT_GENERAL);*/
+
+	/* Ensure that we have a valid GPE number */
+
+	if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) {
+		return (INTERRUPT_NOT_HANDLED);
+	}
+
+	/*
+	 * Disable the GPE.
+	 */
+	acpi_hw_disable_gpe (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
+	 * (see Acpi_ev_asynch_execute_gpe_method).
+	 */
+	if (acpi_gbl_gpe_info [gpe_number].type | ACPI_EVENT_EDGE_TRIGGERED) {
+		acpi_hw_clear_gpe (gpe_number);
+	}
+
+	/*
+	 * Queue-up the Handler:
+	 * ---------------------
+	 * Queue the handler, which is either an installable function handler
+	 * (e.g. EC) or a control method (e.g. _Lxx/_Exx) for later execution.
+	 */
+	if (acpi_gbl_gpe_info [gpe_number].handler ||
+		acpi_gbl_gpe_info [gpe_number].method_handle)
+	{
+		if (ACPI_FAILURE (acpi_os_queue_for_execution (OSD_PRIORITY_GPE,
+				 acpi_ev_asynch_execute_gpe_method,
+				 (void*)(NATIVE_UINT)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 ("Unable to queue-up handler for GPE.");
+		}
+	}
+
+	/*
+	 * Non Handled GPEs:
+	 * -----------------
+	 * GPEs without handlers are disabled and kept that way until a handler
+	 * is registered for them.
+	 */
+	else {
+		REPORT_ERROR ("No installed handler for GPE.");
+	}
+
+	return (INTERRUPT_HANDLED);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)