/*******************************************************************************
 *
 * Module Name: evsci - System Control Interrupt configuration and
 *                      legacy to ACPI mode state transition functions
 *              $Revision: 74 $
 *
 ******************************************************************************/

/*
 *  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 "acnamesp.h"
#include "achware.h"
#include "acevents.h"


#define _COMPONENT          ACPI_EVENTS
	 MODULE_NAME         ("evsci")


/*
 * Elements correspond to counts for TMR, NOT_USED, GBL, PWR_BTN, SLP_BTN, RTC,
 * and GENERAL respectively.  These counts are modified by the ACPI interrupt
 * handler.
 *
 * TBD: [Investigate] Note that GENERAL should probably be split out into
 * one element for each bit in the GPE registers
 */


/*******************************************************************************
 *
 * FUNCTION:    Acpi_ev_sci_handler
 *
 * PARAMETERS:  Context   - Calling Context
 *
 * RETURN:      Status code indicates whether interrupt was handled.
 *
 * DESCRIPTION: Interrupt handler that will figure out what function or
 *              control method to call to deal with a SCI.  Installed
 *              using BU interrupt support.
 *
 ******************************************************************************/

static u32
acpi_ev_sci_handler (void *context)
{
	u32                     interrupt_handled = INTERRUPT_NOT_HANDLED;


	FUNCTION_TRACE("Ev_sci_handler");


	/*
	 * Make sure that ACPI is enabled by checking SCI_EN.  Note that we are
	 * required to treat the SCI interrupt as sharable, level, active low.
	 */
	if (!acpi_hw_register_bit_access (ACPI_READ, ACPI_MTX_DO_NOT_LOCK, SCI_EN)) {
		/* ACPI is not enabled;  this interrupt cannot be for us */

		return_VALUE (INTERRUPT_NOT_HANDLED);
	}

	/*
	 * Fixed Acpi_events:
	 * -------------
	 * Check for and dispatch any Fixed Acpi_events that have occurred
	 */
	interrupt_handled |= acpi_ev_fixed_event_detect ();

	/*
	 * GPEs:
	 * -----
	 * Check for and dispatch any GPEs that have occurred
	 */
	interrupt_handled |= acpi_ev_gpe_detect ();

	return_VALUE (interrupt_handled);
}


/******************************************************************************
 *
 * FUNCTION:    Acpi_ev_install_sci_handler
 *
 * PARAMETERS:  none
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Installs SCI handler.
 *
 ******************************************************************************/

u32
acpi_ev_install_sci_handler (void)
{
	u32                     status = AE_OK;


	FUNCTION_TRACE ("Ev_install_sci_handler");


	status = acpi_os_install_interrupt_handler ((u32) acpi_gbl_FADT->sci_int,
			   acpi_ev_sci_handler, NULL);
	return_ACPI_STATUS (status);
}


/******************************************************************************

 *
 * FUNCTION:    Acpi_ev_remove_sci_handler
 *
 * PARAMETERS:  none
 *
 * RETURN:      E_OK if handler uninstalled OK, E_ERROR if handler was not
 *              installed to begin with
 *
 * DESCRIPTION: Restores original status of all fixed event enable bits and
 *              removes SCI handler.
 *
 ******************************************************************************/

acpi_status
acpi_ev_remove_sci_handler (void)
{
	FUNCTION_TRACE ("Ev_remove_sci_handler");


#if 0
	/* TBD:[Investigate] Figure this out!!  Disable all events first ???  */

	if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (TMR_FIXED_EVENT)) {
		acpi_event_disable_event (TMR_FIXED_EVENT);
	}

	if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (GBL_FIXED_EVENT)) {
		acpi_event_disable_event (GBL_FIXED_EVENT);
	}

	if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (PWR_BTN_FIXED_EVENT)) {
		acpi_event_disable_event (PWR_BTN_FIXED_EVENT);
	}

	if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (SLP_BTN_FIXED_EVENT)) {
		acpi_event_disable_event (SLP_BTN_FIXED_EVENT);
	}

	if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (RTC_FIXED_EVENT)) {
		acpi_event_disable_event (RTC_FIXED_EVENT);
	}

	original_fixed_enable_bit_status = 0;

#endif

	acpi_os_remove_interrupt_handler ((u32) acpi_gbl_FADT->sci_int,
			   acpi_ev_sci_handler);

	return_ACPI_STATUS (AE_OK);
}


/*******************************************************************************
 *
 * FUNCTION:    Acpi_ev_restore_acpi_state
 *
 * PARAMETERS:  none
 *
 * RETURN:      none
 *
 * DESCRIPTION: Restore the original ACPI state of the machine
 *
 ******************************************************************************/

void
acpi_ev_restore_acpi_state (void)
{
	u32                     index;


	FUNCTION_TRACE ("Ev_restore_acpi_state");


	/* Restore the state of the chipset enable bits. */

	if (acpi_gbl_restore_acpi_chipset == TRUE) {
		/* Restore the fixed events */

		if (acpi_hw_register_read (ACPI_MTX_LOCK, PM1_EN) !=
				acpi_gbl_pm1_enable_register_save) {
			acpi_hw_register_write (ACPI_MTX_LOCK, PM1_EN,
				acpi_gbl_pm1_enable_register_save);
		}


		/* Ensure that all status bits are clear */

		acpi_hw_clear_acpi_status ();


		/* Now restore the GPEs */

		for (index = 0; index < DIV_2 (acpi_gbl_FADT->gpe0blk_len); index++) {
			if (acpi_hw_register_read (ACPI_MTX_LOCK, GPE0_EN_BLOCK | index) !=
					acpi_gbl_gpe0enable_register_save[index]) {
				acpi_hw_register_write (ACPI_MTX_LOCK, GPE0_EN_BLOCK | index,
					acpi_gbl_gpe0enable_register_save[index]);
			}
		}

		/* GPE 1 present? */

		if (acpi_gbl_FADT->gpe1_blk_len) {
			for (index = 0; index < DIV_2 (acpi_gbl_FADT->gpe1_blk_len); index++) {
				if (acpi_hw_register_read (ACPI_MTX_LOCK, GPE1_EN_BLOCK | index) !=
					acpi_gbl_gpe1_enable_register_save[index]) {
					acpi_hw_register_write (ACPI_MTX_LOCK, GPE1_EN_BLOCK | index,
						acpi_gbl_gpe1_enable_register_save[index]);
				}
			}
		}

		if (acpi_hw_get_mode() != acpi_gbl_original_mode) {
			acpi_hw_set_mode (acpi_gbl_original_mode);
		}
	}

	return_VOID;
}


/******************************************************************************
 *
 * FUNCTION:    Acpi_ev_terminate
 *
 * PARAMETERS:  none
 *
 * RETURN:      none
 *
 * DESCRIPTION: free memory allocated for table storage.
 *
 ******************************************************************************/

void
acpi_ev_terminate (void)
{

	FUNCTION_TRACE ("Ev_terminate");


	/*
	 * Free global tables, etc.
	 */
	if (acpi_gbl_gpe_registers) {
		ACPI_MEM_FREE (acpi_gbl_gpe_registers);
	}

	if (acpi_gbl_gpe_info) {
		ACPI_MEM_FREE (acpi_gbl_gpe_info);
	}

	return_VOID;
}


