|
From: Andy P. <at...@us...> - 2002-04-09 15:13:44
|
Update of /cvsroot/linux-vax/kernel-2.4/drivers/acpi/ospm/system
In directory usw-pr-cvs1:/tmp/cvs-serv13840/acpi/ospm/system
Added Files:
Makefile sm.c sm_osl.c
Log Message:
synch 2.4.15 commit 17
--- NEW FILE ---
O_TARGET := ospm_$(notdir $(CURDIR)).o
obj-m := $(O_TARGET)
EXTRA_CFLAGS += $(ACPI_CFLAGS)
obj-y := $(patsubst %.c,%.o,$(wildcard *.c))
include $(TOPDIR)/Rules.make
--- NEW FILE ---
/*****************************************************************************
*
* Module Name: sm.c
* $Revision: 1.1 $
*
*****************************************************************************/
/*
* Copyright (C) 2000, 2001 Andrew Grover
*
* 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 "sm.h"
#define _COMPONENT ACPI_SYSTEM
MODULE_NAME ("sm")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: sm_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific system.
*
****************************************************************************/
void
sm_print (
SM_CONTEXT *system)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
PROC_NAME("sm_print");
buffer.length = 256;
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return;
}
/*
* Get the full pathname for this ACPI object.
*/
acpi_get_name(system->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic system information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| System[%02x]:[%p] %s\n", system->device_handle, system->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| states: %cS0 %cS1 %cS2 %cS3 %cS4 %cS5\n", (system->states[0]?'+':'-'), (system->states[1]?'+':'-'), (system->states[2]?'+':'-'), (system->states[3]?'+':'-'), (system->states[4]?'+':'-'), (system->states[5]?'+':'-')));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: sm_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_add_device(
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
SM_CONTEXT *system = NULL;
u8 i, type_a, type_b;
FUNCTION_TRACE("sm_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding system device [%02x].\n", device_handle));
if (!context || *context) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context."));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Allocate a new SM_CONTEXT structure.
*/
system = acpi_os_callocate(sizeof(SM_CONTEXT));
if (!system) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
goto end;
}
system->device_handle = device->handle;
system->acpi_handle = device->acpi_handle;
/*
* Sx States:
* ----------
* Figure out which Sx states are supported.
*/
for (i=0; i<SM_MAX_SYSTEM_STATES; i++) {
if (ACPI_SUCCESS(acpi_hw_obtain_sleep_type_register_data(
i,
&type_a,
&type_b))) {
system->states[i] = TRUE;
}
}
status = sm_osl_add_device(system);
if (ACPI_FAILURE(status)) {
goto end;
}
*context = system;
sm_print(system);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(system);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: sm_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_remove_device (
void **context)
{
acpi_status status = AE_OK;
SM_CONTEXT *system = NULL;
FUNCTION_TRACE("sm_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
system = (SM_CONTEXT*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing system device [%02x].\n", system->device_handle));
status = sm_osl_remove_device(system);
acpi_os_free(system);
*context = NULL;
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: sm_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("sm_initialize");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
/*
* Register driver for the System device.
*/
criteria.type = BM_TYPE_SYSTEM;
driver.notify = &sm_notify;
driver.request = &sm_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: sm_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("sm_terminate");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
/*
* Unregister driver for System devices.
*/
criteria.type = BM_TYPE_SYSTEM;
driver.notify = &sm_notify;
driver.request = &sm_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* FUNCTION: sm_notify
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("sm_notify");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = sm_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = sm_remove_device(context);
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: sm_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
sm_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("sm_request");
/*
* Must have a valid request structure and context.
*/
if (!request || !context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Handle Request:
* ---------------
*/
switch (request->command) {
default:
status = AE_SUPPORT;
break;
}
request->status = status;
return_ACPI_STATUS(status);
}
--- NEW FILE ---
/******************************************************************************
*
* Module Name: sm_osl.c
* $Revision: 1.1 $
*
*****************************************************************************/
/*
* Copyright (C) 2000, 2001 Andrew Grover
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/pm.h>
#include <asm/uaccess.h>
#include <linux/acpi.h>
#include <asm/io.h>
#include <linux/mc146818rtc.h>
#include <linux/delay.h>
#include <acpi.h>
#include "sm.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - ACPI System Driver");
#define SM_PROC_INFO "info"
#define SM_PROC_DSDT "dsdt"
extern struct proc_dir_entry *bm_proc_root;
struct proc_dir_entry *sm_proc_root = NULL;
static void (*sm_pm_power_off)(void) = NULL;
static ssize_t sm_osl_read_dsdt(struct file *, char *, size_t, loff_t *);
static struct file_operations proc_dsdt_operations = {
read: sm_osl_read_dsdt,
};
static acpi_status sm_osl_suspend(u32 state);
struct proc_dir_entry *bm_proc_sleep;
struct proc_dir_entry *bm_proc_alarm;
struct proc_dir_entry *bm_proc_gpe;
static int
sm_osl_proc_read_sleep (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
SM_CONTEXT *system = (SM_CONTEXT*) context;
char *str = page;
int len;
int i;
if (!system)
goto end;
if (off != 0)
goto end;
for (i = 0; i <= ACPI_S5; i++) {
if (system->states[i])
str += sprintf(str,"S%d ", i);
}
str += sprintf(str, "\n");
end:
len = (str - page);
if (len < (off + count))
*eof = 1;
*start = page + off;
len -= off;
if (len > count)
len = count;
if (len < 0)
len = 0;
return (len);
}
int sm_osl_proc_write_sleep (struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
SM_CONTEXT *system = (SM_CONTEXT*) data;
char str[10];
char *strend;
unsigned long value;
if (count > (sizeof(str) - 1))
return -EINVAL;
if (copy_from_user(str,buffer,count))
return -EFAULT;
str[count] = '\0';
value = simple_strtoul(str,&strend,0);
if (str == strend)
return -EINVAL;
if (value == 0 || value >= ACPI_S5)
return -EINVAL;
/*
* make sure that the sleep state is supported
*/
if (system->states[value] != TRUE)
return -EINVAL;
sm_osl_suspend(value);
return (count);
}
/****************************************************************************
*
* FUNCTION: sm_osl_proc_read_info
*
****************************************************************************/
static int
sm_osl_proc_read_info (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
acpi_status status = AE_OK;
SM_CONTEXT *system = NULL;
char *p = page;
int len;
acpi_system_info system_info;
acpi_buffer buffer;
u32 i = 0;
if (!context) {
goto end;
}
system = (SM_CONTEXT*) context;
/* don't get status more than once for a single proc read */
if (off != 0) {
goto end;
}
/*
* Get ACPI CA Information.
*/
buffer.length = sizeof(system_info);
buffer.pointer = &system_info;
status = acpi_get_system_info(&buffer);
if (ACPI_FAILURE(status)) {
p += sprintf(p, "ACPI-CA Version: unknown\n");
}
else {
p += sprintf(p, "ACPI-CA Version: %x\n",
system_info.acpi_ca_version);
}
p += sprintf(p, "Sx States Supported: ");
for (i=0; i<SM_MAX_SYSTEM_STATES; i++) {
if (system->states[i]) {
p += sprintf(p, "S%d ", i);
}
}
p += sprintf(p, "\n");
end:
len = (p - page);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return(len);
}
/****************************************************************************
*
* FUNCTION: sm_osl_read_dsdt
*
****************************************************************************/
static ssize_t
sm_osl_read_dsdt(
struct file *file,
char *buf,
size_t count,
loff_t *ppos)
{
acpi_buffer acpi_buf;
void *data;
size_t size = 0;
acpi_buf.length = 0;
acpi_buf.pointer = NULL;
/* determine what buffer size we will need */
if (acpi_get_table(ACPI_TABLE_DSDT, 1, &acpi_buf) != AE_BUFFER_OVERFLOW) {
return 0;
}
acpi_buf.pointer = kmalloc(acpi_buf.length, GFP_KERNEL);
if (!acpi_buf.pointer) {
return -ENOMEM;
}
/* get the table for real */
if (!ACPI_SUCCESS(acpi_get_table(ACPI_TABLE_DSDT, 1, &acpi_buf))) {
kfree(acpi_buf.pointer);
return 0;
}
if (*ppos < acpi_buf.length) {
data = acpi_buf.pointer + file->f_pos;
size = acpi_buf.length - file->f_pos;
if (size > count)
size = count;
if (copy_to_user(buf, data, size)) {
kfree(acpi_buf.pointer);
return -EFAULT;
}
}
kfree(acpi_buf.pointer);
*ppos += size;
return size;
}
static int
sm_osl_proc_read_alarm (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
char *str = page;
int len;
u32 sec,min,hr;
u32 day,mo,yr;
if (off != 0) goto out;
spin_lock(&rtc_lock);
sec = CMOS_READ(RTC_SECONDS_ALARM);
min = CMOS_READ(RTC_MINUTES_ALARM);
hr = CMOS_READ(RTC_HOURS_ALARM);
#if 0
/* if I ever get an FACP with proper values, maybe I'll enable this code */
if (acpi_gbl_FADT->day_alrm)
day = CMOS_READ(acpi_gbl_FADT->day_alrm);
else
day = CMOS_READ(RTC_DAY_OF_MONTH);
if (acpi_gbl_FADT->mon_alrm)
mo = CMOS_READ(acpi_gbl_FADT->mon_alrm);
else
mo = CMOS_READ(RTC_MONTH);;
if (acpi_gbl_FADT->century)
yr = CMOS_READ(acpi_gbl_FADT->century) * 100 + CMOS_READ(RTC_YEAR);
else
yr = CMOS_READ(RTC_YEAR);
#else
day = CMOS_READ(RTC_DAY_OF_MONTH);
mo = CMOS_READ(RTC_MONTH);
yr = CMOS_READ(RTC_YEAR);
#endif
spin_unlock(&rtc_lock);
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hr);
BCD_TO_BIN(day);
BCD_TO_BIN(mo);
BCD_TO_BIN(yr);
str += sprintf(str,"%4.4u-",yr);
str += (mo > 12) ?
sprintf(str,"**-") :
sprintf(str,"%2.2u-",mo);
str += (day > 31) ?
sprintf(str,"** ") :
sprintf(str,"%2.2u ",day);
str += (hr > 23) ?
sprintf(str,"**:") :
sprintf(str,"%2.2u:",hr);
str += (min > 59) ?
sprintf(str,"**:") :
sprintf(str,"%2.2u:",min);
str += (sec > 59) ?
sprintf(str,"**\n") :
sprintf(str,"%2.2u\n",sec);
out:
len = str - page;
if (len < count) *eof = 1;
else if (len > count) len = count;
if (len < 0) len = 0;
*start = page;
return len;
}
static int get_date_field(char **str, u32 *value)
{
char *next,*strend;
int error = -EINVAL;
/* try to find delimeter, only to insert null;
* the end of string won't have one, but is still valid
*/
next = strpbrk(*str,"- :");
if (next) *next++ = '\0';
*value = simple_strtoul(*str,&strend,10);
/* signal success if we got a good digit */
if (strend != *str) error = 0;
if (next) *str = next;
return error;
}
int sm_osl_proc_write_alarm (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
char buf[30];
char *str = buf;
u32 sec,min,hr;
u32 day,mo,yr;
int adjust = 0;
unsigned char rtc_control;
int error = -EINVAL;
if (count > sizeof(buf) - 1) return -EINVAL;
if (copy_from_user(str,buffer,count)) return -EFAULT;
str[count] = '\0';
/* check for time adjustment */
if (str[0] == '+') {
str++;
adjust = 1;
}
if ((error = get_date_field(&str,&yr))) goto out;
if ((error = get_date_field(&str,&mo))) goto out;
if ((error = get_date_field(&str,&day))) goto out;
if ((error = get_date_field(&str,&hr))) goto out;
if ((error = get_date_field(&str,&min))) goto out;
if ((error = get_date_field(&str,&sec))) goto out;
if (sec > 59) {
min += 1;
sec -= 60;
}
if (min > 59) {
hr += 1;
min -= 60;
}
if (hr > 23) {
day += 1;
hr -= 24;
}
if (day > 31) {
mo += 1;
day -= 31;
}
if (mo > 12) {
yr += 1;
mo -= 12;
}
spin_lock_irq(&rtc_lock);
rtc_control = CMOS_READ(RTC_CONTROL);
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BIN_TO_BCD(yr);
BIN_TO_BCD(mo);
BIN_TO_BCD(day);
BIN_TO_BCD(hr);
BIN_TO_BCD(min);
BIN_TO_BCD(sec);
}
if (adjust) {
yr += CMOS_READ(RTC_YEAR);
mo += CMOS_READ(RTC_MONTH);
day += CMOS_READ(RTC_DAY_OF_MONTH);
hr += CMOS_READ(RTC_HOURS);
min += CMOS_READ(RTC_MINUTES);
sec += CMOS_READ(RTC_SECONDS);
}
spin_unlock_irq(&rtc_lock);
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BCD_TO_BIN(yr);
BCD_TO_BIN(mo);
BCD_TO_BIN(day);
BCD_TO_BIN(hr);
BCD_TO_BIN(min);
BCD_TO_BIN(sec);
}
if (sec > 59) {
min++;
sec -= 60;
}
if (min > 59) {
hr++;
min -= 60;
}
if (hr > 23) {
day++;
hr -= 24;
}
if (day > 31) {
mo++;
day -= 31;
}
if (mo > 12) {
yr++;
mo -= 12;
}
if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BIN_TO_BCD(yr);
BIN_TO_BCD(mo);
BIN_TO_BCD(day);
BIN_TO_BCD(hr);
BIN_TO_BCD(min);
BIN_TO_BCD(sec);
}
spin_lock_irq(&rtc_lock);
/* write the fields the rtc knows about */
CMOS_WRITE(hr,RTC_HOURS_ALARM);
CMOS_WRITE(min,RTC_MINUTES_ALARM);
CMOS_WRITE(sec,RTC_SECONDS_ALARM);
/* If the system supports an enhanced alarm, it will have non-zero
* offsets into the CMOS RAM here.
* Which for some reason are pointing to the RTC area of memory.
*/
#if 0
if (acpi_gbl_FADT->day_alrm) CMOS_WRITE(day,acpi_gbl_FADT->day_alrm);
if (acpi_gbl_FADT->mon_alrm) CMOS_WRITE(mo,acpi_gbl_FADT->mon_alrm);
if (acpi_gbl_FADT->century) CMOS_WRITE(yr / 100,acpi_gbl_FADT->century);
#endif
/* enable the rtc alarm interrupt */
if (!(rtc_control & RTC_AIE)) {
rtc_control |= RTC_AIE;
CMOS_WRITE(rtc_control,RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
}
/* unlock the lock on the rtc now that we're done with it */
spin_unlock_irq(&rtc_lock);
acpi_hw_register_bit_access(ACPI_WRITE,ACPI_MTX_LOCK, RTC_EN, 1);
file->f_pos += count;
error = 0;
out:
return error ? error : count;
}
static int
sm_osl_proc_read_gpe(
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
char *str = page;
int size;
int length;
int i;
u32 addr,data;
if (off) goto out;
if (acpi_gbl_FADT->V1_gpe0blk) {
length = acpi_gbl_FADT->gpe0blk_len / 2;
str += sprintf(str,"GPE0: ");
for (i = length; i > 0; i--) {
addr = GPE0_EN_BLOCK | (i - 1);
data = acpi_hw_register_read(ACPI_MTX_LOCK,addr);
str += sprintf(str,"%2.2x ",data);
}
str += sprintf(str,"\n");
str += sprintf(str,"Status: ");
for (i = length; i > 0; i--) {
addr = GPE0_STS_BLOCK | (i - 1);
data = acpi_hw_register_read(ACPI_MTX_LOCK,addr);
str += sprintf(str,"%2.2x ",data);
}
str += sprintf(str,"\n");
}
if (acpi_gbl_FADT->V1_gpe1_blk) {
length = acpi_gbl_FADT->gpe1_blk_len / 2;
str += sprintf(str,"GPE1: ");
for (i = length; i > 0; i--) {
addr = GPE1_EN_BLOCK | (i - 1);
data = acpi_hw_register_read(ACPI_MTX_LOCK,addr);
str += sprintf(str,"%2.2x",data);
}
str += sprintf(str,"\n");
str += sprintf(str,"Status: ");
for (i = length; i > 0; i--) {
addr = GPE1_STS_BLOCK | (i - 1);
data = acpi_hw_register_read(ACPI_MTX_LOCK,addr);
str += sprintf(str,"%2.2x",data);
}
str += sprintf(str,"\n");
}
out:
size = str - page;
if (size < count) *eof = 1;
else if (size > count) size = count;
if (size < 0) size = 0;
*start = page;
return size;
}
static int
sm_osl_proc_write_gpe (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
char buf[256];
char *str = buf;
char *next;
int error = -EINVAL;
u32 addr,value = 0;
if (count > sizeof(buf) + 1) return -EINVAL;
if (copy_from_user(str,buffer,count)) return -EFAULT;
str[count] = '\0';
/* set addr to which block to refer to */
if (!strncmp(str,"GPE0 ",5)) addr = GPE0_EN_BLOCK;
else if (!strncmp(str,"GPE1 ",5)) addr = GPE1_EN_BLOCK;
else goto out;
str += 5;
/* set low order bits to index of bit to set */
addr |= simple_strtoul(str,&next,0);
if (next == str) goto out;
if (next) {
str = ++next;
value = simple_strtoul(str,&next,0);
if (next == str) value = 1;
}
value = acpi_hw_register_bit_access(ACPI_WRITE,ACPI_MTX_LOCK,addr,(value ? 1 : 0));
error = 0;
out:
return error ? error : count;
}
/****************************************************************************
*
* FUNCTION: sm_osl_suspend
*
* PARAMETERS: %state: Sleep state to enter. Assumed that caller has filtered
* out bogus values, so it's one of S1, S2, S3 or S4
*
* RETURN: ACPI_STATUS, whether or not we successfully entered and
* exited sleep.
*
* DESCRIPTION:
* This function is the meat of the sleep routine, as far as the ACPI-CA is
* concerned.
*
* See Chapter 9 of the ACPI 2.0 spec for details concerning the methodology here.
*
* It will do the following things:
* - Call arch-specific routines to save the processor and kernel state
* - Call acpi_enter_sleep_state to actually go to sleep
* ....
* When we wake back up, we will:
* - Restore the processor and kernel state
* - Return to the user
*
* By having this routine in here, it hides it from every part of the CA,
* so it can remain OS-independent. The only function that calls this is
* sm_proc_write_sleep, which gets the sleep state to enter from the user.
*
****************************************************************************/
static acpi_status
sm_osl_suspend(u32 state)
{
acpi_status status = AE_ERROR;
unsigned long wakeup_address;
/* get out if state is invalid */
if (state < ACPI_S1 || state > ACPI_S5)
goto acpi_sleep_done;
/* make sure we don't get any suprises */
disable();
/* TODO: save device state and suspend them */
/* save the processor state to memory if going into S2 or S3;
* save it to disk if going into S4.
* Also, set the FWV if going into an STR state
*/
if (state == ACPI_S2 || state == ACPI_S3) {
#ifdef DONT_USE_UNTIL_LOWLEVEL_CODE_EXISTS
wakeup_address = acpi_save_state_mem((unsigned long)&&acpi_sleep_done);
if (!wakeup_address) goto acpi_sleep_done;
acpi_set_firmware_waking_vector(
(ACPI_PHYSICAL_ADDRESS)wakeup_address);
#endif
} else if (state == ACPI_S4)
#ifdef DONT_USE_UNTIL_LOWLEVEL_CODE_EXISTS
if (acpi_save_state_disk((unsigned long)&&acpi_sleep_done))
goto acpi_sleep_done;
#endif
/* set status, since acpi_enter_sleep_state won't return unless something
* goes wrong, or it's just S1.
*/
status = AE_OK;
mdelay(10);
status = acpi_enter_sleep_state(state);
acpi_sleep_done:
/* pause for a bit to allow devices to come back on */
mdelay(10);
/* make sure that the firmware waking vector is reset */
acpi_set_firmware_waking_vector((ACPI_PHYSICAL_ADDRESS)0);
acpi_leave_sleep_state(state);
/* TODO: resume devices and restore their state */
enable();
return status;
}
/****************************************************************************
*
* FUNCTION: sm_osl_power_down
*
****************************************************************************/
void
sm_osl_power_down (void)
{
/* Power down the system (S5 = soft off). */
sm_osl_suspend(ACPI_S5);
}
/****************************************************************************
*
* FUNCTION: sm_osl_add_device
*
****************************************************************************/
acpi_status
sm_osl_add_device(
SM_CONTEXT *system)
{
u32 i = 0;
struct proc_dir_entry *bm_proc_dsdt;
if (!system) {
return(AE_BAD_PARAMETER);
}
printk("ACPI: System firmware supports");
for (i=0; i<SM_MAX_SYSTEM_STATES; i++) {
if (system->states[i]) {
printk(" S%d", i);
}
}
printk("\n");
if (system->states[ACPI_STATE_S5]) {
sm_pm_power_off = pm_power_off;
pm_power_off = sm_osl_power_down;
}
create_proc_read_entry(SM_PROC_INFO, S_IRUGO,
sm_proc_root, sm_osl_proc_read_info, (void*)system);
bm_proc_sleep = create_proc_read_entry("sleep", S_IFREG | S_IRUGO | S_IWUSR,
sm_proc_root, sm_osl_proc_read_sleep, (void*)system);
if (bm_proc_sleep)
bm_proc_sleep->write_proc = sm_osl_proc_write_sleep;
bm_proc_alarm = create_proc_read_entry("alarm", S_IFREG | S_IRUGO | S_IWUSR,
sm_proc_root,sm_osl_proc_read_alarm, NULL);
if (bm_proc_alarm)
bm_proc_alarm->write_proc = sm_osl_proc_write_alarm;
bm_proc_gpe = create_proc_read_entry("gpe", S_IFREG | S_IRUGO | S_IWUSR,
sm_proc_root,sm_osl_proc_read_gpe,NULL);
if (bm_proc_gpe)
bm_proc_gpe->write_proc = sm_osl_proc_write_gpe;
/*
* Get a wakeup address for use when we come back from sleep.
* At least on IA-32, this needs to be in low memory.
* When sleep is supported on other arch's, then we may want
* to move this out to another place, but GFP_LOW should suffice
* for now.
*/
#if 0
if (system->states[ACPI_S3] || system->states[ACPI_S4]) {
acpi_wakeup_address = (unsigned long)virt_to_phys(get_free_page(GFP_LOWMEM));
printk(KERN_INFO "ACPI: Have wakeup address 0x%8.8x\n",acpi_wakeup_address);
}
#endif
/*
* This returns more than a page, so we need to use our own file ops,
* not proc's generic ones
*/
bm_proc_dsdt = create_proc_entry(SM_PROC_DSDT, S_IRUSR, sm_proc_root);
if (bm_proc_dsdt) {
bm_proc_dsdt->proc_fops = &proc_dsdt_operations;
}
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: sm_osl_remove_device
*
****************************************************************************/
acpi_status
sm_osl_remove_device (
SM_CONTEXT *system)
{
if (!system) {
return(AE_BAD_PARAMETER);
}
remove_proc_entry(SM_PROC_INFO, sm_proc_root);
remove_proc_entry(SM_PROC_DSDT, sm_proc_root);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: sm_osl_generate_event
*
****************************************************************************/
acpi_status
sm_osl_generate_event (
u32 event,
SM_CONTEXT *system)
{
acpi_status status = AE_OK;
if (!system) {
return(AE_BAD_PARAMETER);
}
switch (event) {
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: sm_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
sm_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
sm_proc_root = bm_proc_root;
if (!sm_proc_root) {
status = AE_ERROR;
}
else {
status = sm_initialize();
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: sm_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
sm_osl_cleanup (void)
{
sm_terminate();
return;
}
module_init(sm_osl_init);
module_exit(sm_osl_cleanup);
|