Commit 412743bb authored by Andy Grover's avatar Andy Grover Committed by Linus Torvalds

[PATCH] ACPI patch 6/9

This removes the old OSPM code. It lived under drivers/acpi/ospm/*, but
the new code just lives in drivers/acpi, and removes some unnecessary
abstraction that this old code had.
parent 90679878
/******************************************************************************
*
* Module Name: os.c - Linux OSL functions
* $Revision: 49 $
*
*****************************************************************************/
/*
* os.c - OS-dependent functions
*
* Copyright (C) 2000 Andrew Henroid
* Copyright (C) 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
*/
/* Changes
*
* Christopher Liebman <liebman@sponsera.com> 2001-5-15
* - Fixed improper kernel_thread parameters
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <acpi.h>
#ifdef CONFIG_ACPI_EFI
#include <asm/efi.h>
#endif
#ifdef _IA64
#include <asm/hw_irq.h>
#endif
#define _COMPONENT ACPI_OS_SERVICES
MODULE_NAME ("os")
typedef struct
{
OSD_EXECUTION_CALLBACK function;
void *context;
} ACPI_OS_DPC;
/*****************************************************************************
* Debugger Stuff
*****************************************************************************/
#ifdef ENABLE_DEBUGGER
#include <linux/kdb.h>
/* stuff for debugger support */
int acpi_in_debugger = 0;
extern NATIVE_CHAR line_buf[80];
#endif
/*****************************************************************************
* Globals
*****************************************************************************/
static int acpi_irq_irq = 0;
static OSD_HANDLER acpi_irq_handler = NULL;
static void *acpi_irq_context = NULL;
/******************************************************************************
* Functions
*****************************************************************************/
acpi_status
acpi_os_initialize(void)
{
return AE_OK;
}
acpi_status
acpi_os_terminate(void)
{
if (acpi_irq_handler) {
acpi_os_remove_interrupt_handler(acpi_irq_irq,
acpi_irq_handler);
}
return AE_OK;
}
s32
acpi_os_printf(const NATIVE_CHAR *fmt,...)
{
s32 size;
va_list args;
va_start(args, fmt);
size = acpi_os_vprintf(fmt, args);
va_end(args);
return size;
}
s32
acpi_os_vprintf(const NATIVE_CHAR *fmt, va_list args)
{
static char buffer[512];
int size = vsprintf(buffer, fmt, args);
#ifdef ENABLE_DEBUGGER
if (acpi_in_debugger) {
kdb_printf("%s", buffer);
} else {
printk("%s", buffer);
}
#else
printk("%s", buffer);
#endif
return size;
}
void *
acpi_os_allocate(u32 size)
{
return kmalloc(size, GFP_KERNEL);
}
void *
acpi_os_callocate(u32 size)
{
void *ptr = acpi_os_allocate(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void
acpi_os_free(void *ptr)
{
kfree(ptr);
}
acpi_status
acpi_os_get_root_pointer(u32 flags, ACPI_PHYSICAL_ADDRESS *phys_addr)
{
#ifndef CONFIG_ACPI_EFI
if (ACPI_FAILURE(acpi_find_root_pointer(flags, phys_addr))) {
printk(KERN_ERR "ACPI: System description tables not found\n");
return AE_ERROR;
}
#else /*CONFIG_ACPI_EFI*/
if (efi.acpi20)
*phys_addr = (ACPI_PHYSICAL_ADDRESS) efi.acpi20;
else if (efi.acpi)
*phys_addr = (ACPI_PHYSICAL_ADDRESS) efi.acpi;
else {
printk(KERN_ERR "ACPI: System description tables not found\n");
*phys_addr = NULL;
return AE_ERROR;
}
#endif /*CONFIG_ACPI_EFI*/
return AE_OK;
}
acpi_status
acpi_os_map_memory(ACPI_PHYSICAL_ADDRESS phys, u32 size, void **virt)
{
if (phys > ULONG_MAX) {
printk(KERN_ERR "ACPI: Cannot map memory that high\n");
return AE_ERROR;
}
*virt = ioremap((unsigned long) phys, size);
if (!*virt)
return AE_ERROR;
return AE_OK;
}
void
acpi_os_unmap_memory(void *virt, u32 size)
{
iounmap(virt);
}
acpi_status
acpi_os_get_physical_address(void *virt, ACPI_PHYSICAL_ADDRESS *phys)
{
if(!phys || !virt)
return AE_BAD_PARAMETER;
*phys = virt_to_phys(virt);
return AE_OK;
}
static void
acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
{
(*acpi_irq_handler)(acpi_irq_context);
}
acpi_status
acpi_os_install_interrupt_handler(u32 irq, OSD_HANDLER handler, void *context)
{
#ifdef _IA64
irq = isa_irq_to_vector(irq);
#endif /*_IA64*/
acpi_irq_irq = irq;
acpi_irq_handler = handler;
acpi_irq_context = context;
if (request_irq(irq,
acpi_irq,
SA_SHIRQ,
"acpi",
acpi_irq)) {
printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n", irq);
return AE_ERROR;
}
return AE_OK;
}
acpi_status
acpi_os_remove_interrupt_handler(u32 irq, OSD_HANDLER handler)
{
if (acpi_irq_handler) {
#ifdef _IA64
irq = isa_irq_to_vector(irq);
#endif /*_IA64*/
free_irq(irq, acpi_irq);
acpi_irq_handler = NULL;
}
return AE_OK;
}
/*
* Running in interpreter thread context, safe to sleep
*/
void
acpi_os_sleep(u32 sec, u32 ms)
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ * sec + (ms * HZ) / 1000);
}
void
acpi_os_stall(u32 us)
{
if (us > 10000) {
mdelay(us / 1000);
}
else {
udelay(us);
}
}
acpi_status
acpi_os_read_port(
ACPI_IO_ADDRESS port,
void *value,
u32 width)
{
u32 dummy;
if (!value)
value = &dummy;
switch (width)
{
case 8:
*(u8*) value = inb(port);
break;
case 16:
*(u16*) value = inw(port);
break;
case 32:
*(u32*) value = inl(port);
break;
default:
BUG();
}
return AE_OK;
}
acpi_status
acpi_os_write_port(
ACPI_IO_ADDRESS port,
NATIVE_UINT value,
u32 width)
{
switch (width)
{
case 8:
outb(value, port);
break;
case 16:
outw(value, port);
break;
case 32:
outl(value, port);
break;
default:
BUG();
}
return AE_OK;
}
acpi_status
acpi_os_read_memory(
ACPI_PHYSICAL_ADDRESS phys_addr,
void *value,
u32 width)
{
u32 dummy;
if (!value)
value = &dummy;
switch (width)
{
case 8:
*(u8*) value = *(u8*) phys_to_virt(phys_addr);
break;
case 16:
*(u16*) value = *(u16*) phys_to_virt(phys_addr);
break;
case 32:
*(u32*) value = *(u32*) phys_to_virt(phys_addr);
break;
default:
BUG();
}
return AE_OK;
}
acpi_status
acpi_os_write_memory(
ACPI_PHYSICAL_ADDRESS phys_addr,
u32 value,
u32 width)
{
switch (width)
{
case 8:
*(u8*) phys_to_virt(phys_addr) = value;
break;
case 16:
*(u16*) phys_to_virt(phys_addr) = value;
break;
case 32:
*(u32*) phys_to_virt(phys_addr) = value;
break;
default:
BUG();
}
return AE_OK;
}
#ifdef CONFIG_ACPI_PCI
/* Architecture-dependent low-level PCI configuration access functions. */
extern int (*pci_config_read)(int seg, int bus, int dev, int fn, int reg, int len, u32 *val);
extern int (*pci_config_write)(int seg, int bus, int dev, int fn, int reg, int len, u32 val);
acpi_status
acpi_os_read_pci_configuration (
acpi_pci_id *pci_id,
u32 reg,
void *value,
u32 width)
{
int result = 0;
if (!value)
return AE_ERROR;
switch (width)
{
case 8:
result = pci_config_read(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 1, value);
break;
case 16:
result = pci_config_read(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 2, value);
break;
case 32:
result = pci_config_read(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 4, value);
break;
default:
BUG();
}
return (result ? AE_ERROR : AE_OK);
}
acpi_status
acpi_os_write_pci_configuration (
acpi_pci_id *pci_id,
u32 reg,
NATIVE_UINT value,
u32 width)
{
int result = 0;
switch (width)
{
case 8:
result = pci_config_write(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 1, value);
break;
case 16:
result = pci_config_write(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 2, value);
break;
case 32:
result = pci_config_write(pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, reg, 4, value);
break;
default:
BUG();
}
return (result ? AE_ERROR : AE_OK);
}
#else /*CONFIG_ACPI_PCI*/
acpi_status
acpi_os_read_pci_configuration (
acpi_pci_id *pci_id,
u32 reg,
void *value,
u32 width)
{
int devfn = PCI_DEVFN(pci_id->device, pci_id->function);
struct pci_dev *dev = pci_find_slot(pci_id->bus, devfn);
if (!value || !dev)
return AE_ERROR;
switch (width)
{
case 8:
if (pci_read_config_byte(dev, reg, (u8*) value))
return AE_ERROR;
break;
case 16:
if (pci_read_config_word(dev, reg, (u16*) value))
return AE_ERROR;
break;
case 32:
if (pci_read_config_dword(dev, reg, (u32*) value))
return AE_ERROR;
break;
default:
BUG();
}
return AE_OK;
}
acpi_status
acpi_os_write_pci_configuration (
acpi_pci_id *pci_id,
u32 reg,
u32 value,
u32 width)
{
int devfn = PCI_DEVFN(pci_id->device, pci_id->function);
struct pci_dev *dev = pci_find_slot(pci_id->bus, devfn);
if (!dev)
return AE_ERROR;
switch (width)
{
case 8:
if (pci_write_config_byte(dev, reg, value))
return AE_ERROR;
break;
case 16:
if (pci_write_config_word(dev, reg, value))
return AE_ERROR;
break;
case 32:
if (pci_write_config_dword(dev, reg, value))
return AE_ERROR;
break;
default:
BUG();
}
return AE_OK;
}
#endif /*CONFIG_ACPI_PCI*/
acpi_status
acpi_os_load_module (
char *module_name)
{
PROC_NAME("acpi_os_load_module");
if (!module_name)
return AE_BAD_PARAMETER;
if (0 > request_module(module_name)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to load module [%s].\n", module_name));
return AE_ERROR;
}
return AE_OK;
}
acpi_status
acpi_os_unload_module (
char *module_name)
{
if (!module_name)
return AE_BAD_PARAMETER;
/* TODO: How on Linux? */
/* this is done automatically for all modules with
use_count = 0, I think. see: MOD_INC_USE_COUNT -ASG */
return AE_OK;
}
/*
* See acpi_os_queue_for_execution()
*/
static int
acpi_os_queue_exec (
void *context)
{
ACPI_OS_DPC *dpc = (ACPI_OS_DPC*)context;
PROC_NAME("acpi_os_queue_exec");
daemonize();
strcpy(current->comm, "kacpidpc");
if (!dpc || !dpc->function)
return AE_BAD_PARAMETER;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Executing function [%p(%p)].\n", dpc->function, dpc->context));
dpc->function(dpc->context);
kfree(dpc);
return 1;
}
static void
acpi_os_schedule_exec (
void *context)
{
ACPI_OS_DPC *dpc = NULL;
int thread_pid = -1;
PROC_NAME("acpi_os_schedule_exec");
dpc = (ACPI_OS_DPC*)context;
if (!dpc) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Creating new thread to run function [%p(%p)].\n", dpc->function, dpc->context));
thread_pid = kernel_thread(acpi_os_queue_exec, dpc,
(CLONE_FS | CLONE_FILES | SIGCHLD));
if (thread_pid < 0) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to kernel_thread() failed.\n"));
acpi_os_free(dpc);
}
}
acpi_status
acpi_os_queue_for_execution(
u32 priority,
OSD_EXECUTION_CALLBACK function,
void *context)
{
acpi_status status = AE_OK;
ACPI_OS_DPC *dpc = NULL;
PROC_NAME("acpi_os_queue_for_execution");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Scheduling function [%p(%p)] for deferred execution.\n", function, context));
if (!function)
return AE_BAD_PARAMETER;
/*
* Queue via DPC:
* --------------
* Note that we have to use two different processes for queuing DPCs:
* Interrupt-Level: Use schedule_task; can't spawn a new thread.
* Kernel-Level: Spawn a new kernel thread, as schedule_task has
* its limitations (e.g. single-threaded model), and
* all other task queues run at interrupt-level.
*/
switch (priority) {
case OSD_PRIORITY_GPE:
{
static struct tq_struct task;
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee.
*/
dpc = kmalloc(sizeof(ACPI_OS_DPC), GFP_ATOMIC);
if (!dpc)
return AE_NO_MEMORY;
dpc->function = function;
dpc->context = context;
memset(&task, 0, sizeof(struct tq_struct));
task.routine = acpi_os_schedule_exec;
task.data = (void*)dpc;
if (schedule_task(&task) < 0) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to schedule_task() failed.\n"));
status = AE_ERROR;
}
}
break;
default:
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee.
*/
dpc = kmalloc(sizeof(ACPI_OS_DPC), GFP_KERNEL);
if (!dpc)
return AE_NO_MEMORY;
dpc->function = function;
dpc->context = context;
acpi_os_schedule_exec(dpc);
break;
}
return status;
}
acpi_status
acpi_os_create_semaphore(
u32 max_units,
u32 initial_units,
acpi_handle *handle)
{
struct semaphore *sem = NULL;
PROC_NAME("acpi_os_create_semaphore");
sem = acpi_os_callocate(sizeof(struct semaphore));
if (!sem)
return AE_NO_MEMORY;
sema_init(sem, initial_units);
*handle = (acpi_handle*)sem;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Creating semaphore[%p|%d].\n", *handle, initial_units));
return AE_OK;
}
/*
* TODO: A better way to delete semaphores? Linux doesn't have a
* 'delete_semaphore()' function -- may result in an invalid
* pointer dereference for non-synchronized consumers. Should
* we at least check for blocked threads and signal/cancel them?
*/
acpi_status
acpi_os_delete_semaphore(
acpi_handle handle)
{
struct semaphore *sem = (struct semaphore*) handle;
PROC_NAME("acpi_os_delete_semaphore");
if (!sem)
return AE_BAD_PARAMETER;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Deleting semaphore[%p].\n", handle));
acpi_os_free(sem); sem = NULL;
return AE_OK;
}
/*
* TODO: The kernel doesn't have a 'down_timeout' function -- had to
* improvise. The process is to sleep for one scheduler quantum
* until the semaphore becomes available. Downside is that this
* may result in starvation for timeout-based waits when there's
* lots of semaphore activity.
*
* TODO: Support for units > 1?
*/
acpi_status
acpi_os_wait_semaphore(
acpi_handle handle,
u32 units,
u32 timeout)
{
acpi_status status = AE_OK;
struct semaphore *sem = (struct semaphore*)handle;
int ret = 0;
PROC_NAME("acpi_os_wait_semaphore");
if (!sem || (units < 1))
return AE_BAD_PARAMETER;
if (units > 1)
return AE_SUPPORT;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Waiting for semaphore[%p|%d|%d]\n", handle, units, timeout));
switch (timeout)
{
/*
* No Wait:
* --------
* A zero timeout value indicates that we shouldn't wait - just
* acquire the semaphore if available otherwise return AE_TIME
* (a.k.a. 'would block').
*/
case 0:
if(down_trylock(sem))
status = AE_TIME;
break;
/*
* Wait Indefinitely:
* ------------------
*/
case WAIT_FOREVER:
ret = down_interruptible(sem);
if (ret < 0)
status = AE_ERROR;
break;
/*
* Wait w/ Timeout:
* ----------------
*/
default:
// TODO: A better timeout algorithm?
{
int i = 0;
static const int quantum_ms = 1000/HZ;
ret = down_trylock(sem);
for (i = timeout; (i > 0 && ret < 0); i -= quantum_ms) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
ret = down_trylock(sem);
}
if (ret != 0)
status = AE_TIME;
}
break;
}
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Failed to acquire semaphore[%p|%d|%d]\n", handle, units, timeout));
}
else {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Acquired semaphore[%p|%d|%d]\n", handle, units, timeout));
}
return status;
}
/*
* TODO: Support for units > 1?
*/
acpi_status
acpi_os_signal_semaphore(
acpi_handle handle,
u32 units)
{
struct semaphore *sem = (struct semaphore *) handle;
PROC_NAME("acpi_os_signal_semaphore");
if (!sem || (units < 1))
return AE_BAD_PARAMETER;
if (units > 1)
return AE_SUPPORT;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Signaling semaphore[%p|%d]\n", handle, units));
up(sem);
return AE_OK;
}
u32
acpi_os_get_line(NATIVE_CHAR *buffer)
{
#ifdef ENABLE_DEBUGGER
if (acpi_in_debugger) {
u32 chars;
kdb_read(buffer, sizeof(line_buf));
/* remove the CR kdb includes */
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
#endif
return 0;
}
/*
* We just have to assume we're dealing with valid memory
*/
BOOLEAN
acpi_os_readable(void *ptr, u32 len)
{
return 1;
}
BOOLEAN
acpi_os_writable(void *ptr, u32 len)
{
return 1;
}
u32
acpi_os_get_thread_id (void)
{
if (!in_interrupt())
return current->pid;
return 0;
}
acpi_status
acpi_os_signal (
u32 function,
void *info)
{
switch (function)
{
case ACPI_SIGNAL_FATAL:
printk(KERN_ERR "ACPI: Fatal opcode executed\n");
break;
case ACPI_SIGNAL_BREAKPOINT:
{
char *bp_info = (char*) info;
printk(KERN_ERR "ACPI breakpoint: %s\n", bp_info);
}
default:
break;
}
return AE_OK;
}
acpi_status
acpi_os_breakpoint(NATIVE_CHAR *msg)
{
acpi_os_printf("breakpoint: %s", msg);
return AE_OK;
}
#
# Makefile for the Linux OSPM code.
#
O_TARGET := $(notdir $(CURDIR)).o
ACPI_CFLAGS += -I$(CURDIR)/include
EXTRA_CFLAGS += $(ACPI_CFLAGS)
subdir-$(CONFIG_ACPI_BUSMGR) += busmgr
subdir-$(CONFIG_ACPI_EC) += ec
subdir-$(CONFIG_ACPI_SYS) += system
subdir-$(CONFIG_ACPI_CPU) += processor
subdir-$(CONFIG_ACPI_CMBATT) += battery
subdir-$(CONFIG_ACPI_AC) += ac_adapter
subdir-$(CONFIG_ACPI_BUTTON) += button
subdir-$(CONFIG_ACPI_THERMAL) += thermal
obj-y += $(foreach dir,$(subdir-y),$(dir)/ospm_$(dir).o)
include $(TOPDIR)/Rules.make
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
/*****************************************************************************
*
* Module Name: ac.c
* $Revision: 23 $
*
*****************************************************************************/
/*
* 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 "ac.h"
#define _COMPONENT ACPI_AC_ADAPTER
MODULE_NAME ("ac")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: ac_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific ac_adapter.
*
****************************************************************************/
void
ac_print (
AC_CONTEXT *ac_adapter)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
PROC_NAME("ac_print");
if (!ac_adapter) {
return;
}
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(ac_adapter->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic adapter information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| AC Adapter[%02x]:[%p] %s\n", ac_adapter->device_handle, ac_adapter->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: ac_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_add_device(
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
AC_CONTEXT *ac_adapter = NULL;
acpi_device_info info;
FUNCTION_TRACE("ac_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding ac_adapter device [%02x].\n", device_handle));
if (!context || *context) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context."));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new AC_CONTEXT structure.
*/
ac_adapter = acpi_os_callocate(sizeof(AC_CONTEXT));
if (!ac_adapter) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
ac_adapter->device_handle = device->handle;
ac_adapter->acpi_handle = device->acpi_handle;
/*
* Get information on this object.
*/
status = acpi_get_object_info(ac_adapter->acpi_handle, &info);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to get object info for ac_adapter device."));
goto end;
}
/*
* _UID?
* -----
*/
if (info.valid & ACPI_VALID_UID) {
strncpy(ac_adapter->uid, info.unique_id, sizeof(info.unique_id));
}
else {
strncpy(ac_adapter->uid, "0", sizeof("0"));
}
/*
* _STA?
* -----
*/
if (!(info.valid & ACPI_VALID_STA)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Must have valid _STA.\n"));
status = AE_ERROR;
goto end;
}
status = ac_osl_add_device(ac_adapter);
if (ACPI_FAILURE(status)) {
goto end;
}
*context = ac_adapter;
ac_print(ac_adapter);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(ac_adapter);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ac_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_remove_device (
void **context)
{
acpi_status status = AE_OK;
AC_CONTEXT *ac_adapter = NULL;
FUNCTION_TRACE("ac_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ac_adapter = (AC_CONTEXT*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing ac_adapter device [%02x].\n", ac_adapter->device_handle));
ac_osl_remove_device(ac_adapter);
acpi_os_free(ac_adapter);
*context = NULL;
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: ac_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("ac_initialize");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
driver.notify = &ac_notify;
driver.request = &ac_request;
/*
* Register driver for AC Adapter devices.
*/
MEMCPY(criteria.hid, AC_HID_AC_ADAPTER, sizeof(AC_HID_AC_ADAPTER));
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ac_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("ac_terminate");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
/*
* Unregister driver for AC Adapter devices.
*/
MEMCPY(criteria.hid, AC_HID_AC_ADAPTER, sizeof(AC_HID_AC_ADAPTER));
driver.notify = &ac_notify;
driver.request = &ac_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* FUNCTION: ac_notify
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ac_notify");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = ac_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = ac_remove_device(context);
break;
case AC_NOTIFY_STATUS_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Status change event detected.\n"));
status = ac_osl_generate_event(notify_type,
((AC_CONTEXT*)*context));
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ac_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ac_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ac_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);
}
/*****************************************************************************
*
* Module Name: ac_osl.c
* $Revision: 10 $
*
*****************************************************************************/
/*
* 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 <acpi.h>
#include "ac.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - AC Adapter Driver");
MODULE_LICENSE("GPL");
#define AC_PROC_ROOT "ac_adapter"
#define AC_PROC_STATUS "status"
#define AC_ON_LINE "on-line"
#define AC_OFF_LINE "off-line"
extern struct proc_dir_entry *bm_proc_root;
static struct proc_dir_entry *ac_proc_root = NULL;
/****************************************************************************
*
* FUNCTION: ac_osl_proc_read_status
*
****************************************************************************/
static int
ac_osl_proc_read_status (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
acpi_status status = AE_OK;
AC_CONTEXT *ac_adapter = NULL;
char *p = page;
int len;
if (!context) {
goto end;
}
ac_adapter = (AC_CONTEXT*)context;
/* don't get status more than once for a single proc read */
if (off != 0) {
goto end;
}
status = bm_evaluate_simple_integer(ac_adapter->acpi_handle,
"_PSR", &(ac_adapter->is_online));
if (ACPI_FAILURE(status)) {
p += sprintf(p, "Error reading AC Adapter status\n");
goto end;
}
if (ac_adapter->is_online) {
p += sprintf(p, "Status: %s\n",
AC_ON_LINE);
}
else {
p += sprintf(p, "Status: %s\n",
AC_OFF_LINE);
}
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: ac_osl_add_device
*
****************************************************************************/
acpi_status
ac_osl_add_device(
AC_CONTEXT *ac_adapter)
{
struct proc_dir_entry *proc_entry = NULL;
if (!ac_adapter) {
return(AE_BAD_PARAMETER);
}
printk(KERN_INFO "ACPI: AC Adapter found\n");
proc_entry = proc_mkdir(ac_adapter->uid, ac_proc_root);
if (!proc_entry) {
return(AE_ERROR);
}
create_proc_read_entry(AC_PROC_STATUS, S_IFREG | S_IRUGO,
proc_entry, ac_osl_proc_read_status, (void*)ac_adapter);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: ac_osl_remove_device
*
****************************************************************************/
acpi_status
ac_osl_remove_device (
AC_CONTEXT *ac_adapter)
{
char proc_entry[64];
if (!ac_adapter) {
return(AE_BAD_PARAMETER);
}
sprintf(proc_entry, "%s/%s", ac_adapter->uid, AC_PROC_STATUS);
remove_proc_entry(proc_entry, ac_proc_root);
sprintf(proc_entry, "%s", ac_adapter->uid);
remove_proc_entry(proc_entry, ac_proc_root);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: ac_osl_generate_event
*
****************************************************************************/
acpi_status
ac_osl_generate_event (
u32 event,
AC_CONTEXT *ac_adapter)
{
acpi_status status = AE_OK;
if (!ac_adapter) {
return(AE_BAD_PARAMETER);
}
switch (event) {
case AC_NOTIFY_STATUS_CHANGE:
status = bm_osl_generate_event(ac_adapter->device_handle,
AC_PROC_ROOT, ac_adapter->uid, event, 0);
break;
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: ac_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
ac_osl_init (void)
{
acpi_status status = AE_OK;
ac_proc_root = proc_mkdir(AC_PROC_ROOT, bm_proc_root);
if (!ac_proc_root) {
status = AE_ERROR;
}
else {
status = ac_initialize();
if (ACPI_FAILURE(status)) {
remove_proc_entry(AC_PROC_ROOT, bm_proc_root);
}
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: ac_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
ac_osl_cleanup (void)
{
ac_terminate();
if (ac_proc_root) {
remove_proc_entry(AC_PROC_ROOT, bm_proc_root);
}
return;
}
module_init(ac_osl_init);
module_exit(ac_osl_cleanup);
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
/*****************************************************************************
*
* Module Name: bt.c
* $Revision: 29 $
*
*****************************************************************************/
/*
* 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 "bt.h"
#define _COMPONENT ACPI_BATTERY
MODULE_NAME ("bt")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bt_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific battery.
*
****************************************************************************/
void
bt_print (
BT_CONTEXT *battery)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
PROC_NAME("bt_print");
if (!battery) {
return;
}
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(battery->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic battery information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Battery[%02x]:[%p] %s\n", battery->device_handle, battery->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| uid[%s] is_present[%d] power_units[%s]\n", battery->uid, battery->is_present, battery->power_units));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: bt_get_info
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
* NOTES: Allocates battery_info - which must be freed by the caller.
*
****************************************************************************/
acpi_status
bt_get_info (
BT_CONTEXT *battery,
BT_BATTERY_INFO **battery_info)
{
acpi_status status = AE_OK;
acpi_buffer bif_buffer, package_format, package_data;
acpi_object *package = NULL;
FUNCTION_TRACE("bt_get_info");
if (!battery || !battery_info || *battery_info) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
memset(&bif_buffer, 0, sizeof(acpi_buffer));
/*
* Evalute _BIF:
* -------------
* And be sure to deallocate bif_buffer.pointer!
*/
status = bm_evaluate_object(battery->acpi_handle, "_BIF", NULL,
&bif_buffer);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Extract Package Data:
* ---------------------
* Type-cast this bif_buffer to a package and use helper
* functions to convert results into BT_BATTERY_INFO structure.
* The first attempt is just to get the size of the package
* data; the second gets the data (once we know the required
* bif_buffer size).
*/
status = bm_cast_buffer(&bif_buffer, (void**)&package,
sizeof(acpi_object));
if (ACPI_FAILURE(status)) {
goto end;
}
package_format.length = sizeof("NNNNNNNNNSSSS");
package_format.pointer = "NNNNNNNNNSSSS";
memset(&package_data, 0, sizeof(acpi_buffer));
status = bm_extract_package_data(package, &package_format,
&package_data);
if (status != AE_BUFFER_OVERFLOW) {
if (status == AE_OK) {
status = AE_ERROR;
}
goto end;
}
package_data.pointer = acpi_os_callocate(package_data.length);
if (!package_data.pointer) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
status = bm_extract_package_data(package, &package_format,
&package_data);
if (ACPI_FAILURE(status)) {
acpi_os_free(package_data.pointer);
goto end;
}
*battery_info = package_data.pointer;
end:
acpi_os_free(bif_buffer.pointer);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bt_get_status
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_get_status (
BT_CONTEXT *battery,
BT_BATTERY_STATUS **battery_status)
{
acpi_status status = AE_OK;
acpi_buffer bst_buffer, package_format, package_data;
acpi_object *package = NULL;
FUNCTION_TRACE("bt_get_status");
if (!battery || !battery_status || *battery_status) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
memset(&bst_buffer, 0, sizeof(acpi_buffer));
/*
* Evalute _BST:
* -------------
* And be sure to deallocate bst_buffer.pointer!
*/
status = bm_evaluate_object(battery->acpi_handle, "_BST",
NULL, &bst_buffer);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Extract Package Data:
* ---------------------
* Type-cast this bst_buffer to a package and use helper
* functions to convert results into BT_BATTERY_STATUS structure.
* The first attempt is just to get the size of the package data;
* the second gets the data (once we know the required bst_buffer
* size).
*/
status = bm_cast_buffer(&bst_buffer, (void**)&package,
sizeof(acpi_object));
if (ACPI_FAILURE(status)) {
goto end;
}
package_format.length = sizeof("NNNN");
package_format.pointer = "NNNN";
memset(&package_data, 0, sizeof(acpi_buffer));
status = bm_extract_package_data(package, &package_format,
&package_data);
if (status != AE_BUFFER_OVERFLOW) {
if (status == AE_OK) {
status = AE_ERROR;
}
goto end;
}
package_data.pointer = acpi_os_callocate(package_data.length);
if (!package_data.pointer) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
status = bm_extract_package_data(package, &package_format,
&package_data);
if (ACPI_FAILURE(status)) {
acpi_os_free(package_data.pointer);
goto end;
}
*battery_status = package_data.pointer;
end:
acpi_os_free(bst_buffer.pointer);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bt_check_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_check_device (
BT_CONTEXT *battery)
{
acpi_status status = AE_OK;
BM_DEVICE_STATUS battery_status = BM_STATUS_UNKNOWN;
u32 was_present = FALSE;
BT_BATTERY_INFO *battery_info = NULL;
FUNCTION_TRACE("bt_check_device");
if (!battery) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
was_present = battery->is_present;
/*
* Battery Present?
* ----------------
* Get the device status and check if battery slot is occupied.
*/
status = bm_get_device_status(battery->device_handle, &battery_status);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Unable to get battery status.\n"));
return_ACPI_STATUS(status);
}
if (battery_status & BM_STATUS_BATTERY_PRESENT) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Battery socket occupied.\n"));
battery->is_present = TRUE;
}
else {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Battery socket not occupied.\n"));
battery->is_present = FALSE;
}
/*
* Battery Appeared?
* -----------------
*/
if (!was_present && battery->is_present) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Battery insertion detected.\n"));
/*
* Units of Power?
* ---------------
* Get the 'units of power', as we'll need this to report
* status information.
*/
status = bt_get_info(battery, &battery_info);
if (ACPI_SUCCESS(status)) {
battery->power_units = (battery_info->power_unit)
? BT_POWER_UNITS_AMPS : BT_POWER_UNITS_WATTS;
acpi_os_free(battery_info);
}
}
/*
* Battery Disappeared?
* --------------------
*/
else if (was_present && !battery->is_present) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Battery removal detected.\n"));
battery->power_units = BT_POWER_UNITS_DEFAULT;
}
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* FUNCTION: bt_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_add_device (
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
BT_CONTEXT *battery = NULL;
FUNCTION_TRACE("bt_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding battery device [%02x].\n", device_handle));
if (!context || *context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new BT_CONTEXT structure.
*/
battery = acpi_os_callocate(sizeof(BT_CONTEXT));
if (!battery) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
battery->device_handle = device->handle;
battery->acpi_handle = device->acpi_handle;
strncpy(battery->uid, device->id.uid, sizeof(battery->uid));
battery->power_units = BT_POWER_UNITS_DEFAULT;
battery->is_present = FALSE;
/*
* See if battery is really present.
*/
status = bt_check_device(battery);
if (ACPI_FAILURE(status)) {
goto end;
}
status = bt_osl_add_device(battery);
if (ACPI_FAILURE(status)) {
goto end;
}
*context = battery;
bt_print(battery);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(battery);
}
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* FUNCTION: bt_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_remove_device (
void **context)
{
acpi_status status = AE_OK;
BT_CONTEXT *battery = NULL;
FUNCTION_TRACE("bt_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
battery = (BT_CONTEXT*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing battery device [%02x].\n", battery->device_handle));
bt_osl_remove_device(battery);
acpi_os_free(battery);
*context = NULL;
return_ACPI_STATUS(status);
}
/*****************************************************************************
* External Functions
*****************************************************************************/
/*****************************************************************************
*
* FUNCTION: bt_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bt_initialize");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Register driver for driver method battery devices.
*/
MEMCPY(criteria.hid, BT_HID_CM_BATTERY, sizeof(BT_HID_CM_BATTERY));
driver.notify = &bt_notify;
driver.request = &bt_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bt_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bt_terminate");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Unregister driver for driver method battery devices.
*/
MEMCPY(criteria.hid, BT_HID_CM_BATTERY, sizeof(BT_HID_CM_BATTERY));
driver.notify = &bt_notify;
driver.request = &bt_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bt_notify
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bt_notify");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = bt_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = bt_remove_device(context);
break;
case BT_NOTIFY_STATUS_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Status change (_BST) event detected.\n"));
status = bt_osl_generate_event(notify_type,
((BT_CONTEXT*)*context));
break;
case BT_NOTIFY_INFORMATION_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Information change (_BIF) event detected.\n"));
status = bt_check_device((BT_CONTEXT*)*context);
if (ACPI_SUCCESS(status)) {
status = bt_osl_generate_event(notify_type,
((BT_CONTEXT*)*context));
}
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bt_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bt_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bt_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);
}
/******************************************************************************
*
* Module Name: bt_osl.c
* $Revision: 24 $
*
*****************************************************************************/
/*
* 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
*/
/*
* Changes:
* Brendan Burns <bburns@wso.williams.edu> 2000-11-15
* - added proc battery interface
* - parse returned data from _BST and _BIF
* Andy Grover <andrew.grover@intel.com> 2000-12-8
* - improved proc interface
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <acpi.h>
#include "bt.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - Control Method Battery Driver");
MODULE_LICENSE("GPL");
#define BT_PROC_ROOT "battery"
#define BT_PROC_STATUS "status"
#define BT_PROC_INFO "info"
extern struct proc_dir_entry *bm_proc_root;
static struct proc_dir_entry *bt_proc_root = NULL;
/****************************************************************************
*
* FUNCTION: bt_osl_proc_read_info
*
****************************************************************************/
static int
bt_osl_proc_read_info (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
BT_CONTEXT *battery = NULL;
BT_BATTERY_INFO *battery_info = NULL;
char *p = page;
int len = 0;
if (!context || (off != 0)) {
goto end;
}
battery = (BT_CONTEXT*)context;
/*
* Battery Present?
* ----------------
*/
if (!battery->is_present) {
p += sprintf(p, "Present: no\n");
goto end;
}
else {
p += sprintf(p, "Present: yes\n");
}
/*
* Get Battery Information:
* ------------------------
*/
if (ACPI_FAILURE(bt_get_info(battery, &battery_info))) {
p += sprintf(p, "Error reading battery information (_BIF)\n");
goto end;
}
if (battery_info->design_capacity == BT_UNKNOWN) {
p += sprintf(p, "Design Capacity: unknown\n");
}
else {
p += sprintf(p, "Design Capacity: %d %sh\n",
(u32)battery_info->design_capacity,
battery->power_units);
}
if (battery_info->last_full_capacity == BT_UNKNOWN) {
p += sprintf(p, "Last Full Capacity: unknown\n");
}
else {
p += sprintf(p, "Last Full Capacity: %d %sh\n",
(u32)battery_info->last_full_capacity,
battery->power_units);
}
if (battery_info->battery_technology == 0) {
p += sprintf(p, "Battery Technology: primary (non-rechargeable)\n");
}
else if (battery_info->battery_technology == 1) {
p += sprintf(p, "Battery Technology: secondary (rechargeable)\n");
}
else {
p += sprintf(p, "Battery Technology: unknown\n");
}
if (battery_info->design_voltage == BT_UNKNOWN) {
p += sprintf(p, "Design Voltage: unknown\n");
}
else {
p += sprintf(p, "Design Voltage: %d mV\n",
(u32)battery_info->design_voltage);
}
p += sprintf(p, "Design Capacity Warning: %d %sh\n",
(u32)battery_info->design_capacity_warning,
battery->power_units);
p += sprintf(p, "Design Capacity Low: %d %sh\n",
(u32)battery_info->design_capacity_low,
battery->power_units);
p += sprintf(p, "Capacity Granularity 1: %d %sh\n",
(u32)battery_info->battery_capacity_granularity_1,
battery->power_units);
p += sprintf(p, "Capacity Granularity 2: %d %sh\n",
(u32)battery_info->battery_capacity_granularity_2,
battery->power_units);
p += sprintf(p, "Model Number: %s\n",
battery_info->model_number);
p += sprintf(p, "Serial Number: %s\n",
battery_info->serial_number);
p += sprintf(p, "Battery Type: %s\n",
battery_info->battery_type);
p += sprintf(p, "OEM Info: %s\n",
battery_info->oem_info);
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;
acpi_os_free(battery_info);
return(len);
}
/****************************************************************************
*
* FUNCTION: bt_osl_proc_read_status
*
****************************************************************************/
static int
bt_osl_proc_read_status (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
BT_CONTEXT *battery = NULL;
BT_BATTERY_STATUS *battery_status = NULL;
char *p = page;
int len = 0;
if (!context || (off != 0)) {
goto end;
}
battery = (BT_CONTEXT*)context;
/*
* Battery Present?
* ----------------
*/
if (!battery->is_present) {
p += sprintf(p, "Present: no\n");
goto end;
}
else {
p += sprintf(p, "Present: yes\n");
}
/*
* Get Battery Status:
* -------------------
*/
if (ACPI_FAILURE(bt_get_status(battery, &battery_status))) {
p += sprintf(p, "Error reading battery status (_BST)\n");
goto end;
}
/*
* Store Data:
* -----------
*/
if (!battery_status->state) {
p += sprintf(p, "State: ok\n");
}
else {
if (battery_status->state & 0x1)
p += sprintf(p, "State: discharging\n");
if (battery_status->state & 0x2)
p += sprintf(p, "State: charging\n");
if (battery_status->state & 0x4)
p += sprintf(p, "State: critically low\n");
}
if (battery_status->present_rate == BT_UNKNOWN) {
p += sprintf(p, "Present Rate: unknown\n");
}
else {
p += sprintf(p, "Present Rate: %d %s\n",
(u32)battery_status->present_rate,
battery->power_units);
}
if (battery_status->remaining_capacity == BT_UNKNOWN) {
p += sprintf(p, "Remaining Capacity: unknown\n");
}
else {
p += sprintf(p, "Remaining Capacity: %d %sh\n",
(u32)battery_status->remaining_capacity,
battery->power_units);
}
if (battery_status->present_voltage == BT_UNKNOWN) {
p += sprintf(p, "Battery Voltage: unknown\n");
}
else {
p += sprintf(p, "Battery Voltage: %d mV\n",
(u32)battery_status->present_voltage);
}
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;
acpi_os_free(battery_status);
return(len);
}
/****************************************************************************
*
* FUNCTION: bt_osl_add_device
*
****************************************************************************/
acpi_status
bt_osl_add_device(
BT_CONTEXT *battery)
{
struct proc_dir_entry *proc_entry = NULL;
if (!battery) {
return(AE_BAD_PARAMETER);
}
if (battery->is_present) {
printk("ACPI: Battery socket found, battery present\n");
}
else {
printk("ACPI: Battery socket found, battery absent\n");
}
proc_entry = proc_mkdir(battery->uid, bt_proc_root);
if (!proc_entry) {
return(AE_ERROR);
}
create_proc_read_entry(BT_PROC_STATUS, S_IFREG | S_IRUGO,
proc_entry, bt_osl_proc_read_status, (void*)battery);
create_proc_read_entry(BT_PROC_INFO, S_IFREG | S_IRUGO,
proc_entry, bt_osl_proc_read_info, (void*)battery);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bt_osl_remove_device
*
****************************************************************************/
acpi_status
bt_osl_remove_device (
BT_CONTEXT *battery)
{
char proc_entry[64];
if (!battery) {
return(AE_BAD_PARAMETER);
}
sprintf(proc_entry, "%s/%s", battery->uid, BT_PROC_INFO);
remove_proc_entry(proc_entry, bt_proc_root);
sprintf(proc_entry, "%s/%s", battery->uid, BT_PROC_STATUS);
remove_proc_entry(proc_entry, bt_proc_root);
sprintf(proc_entry, "%s", battery->uid);
remove_proc_entry(proc_entry, bt_proc_root);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bt_osl_generate_event
*
****************************************************************************/
acpi_status
bt_osl_generate_event (
u32 event,
BT_CONTEXT *battery)
{
acpi_status status = AE_OK;
if (!battery) {
return(AE_BAD_PARAMETER);
}
switch (event) {
case BT_NOTIFY_STATUS_CHANGE:
case BT_NOTIFY_INFORMATION_CHANGE:
status = bm_osl_generate_event(battery->device_handle,
BT_PROC_ROOT, battery->uid, event, 0);
break;
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: bt_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
bt_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
bt_proc_root = proc_mkdir(BT_PROC_ROOT, bm_proc_root);
if (!bt_proc_root) {
status = AE_ERROR;
}
else {
status = bt_initialize();
if (ACPI_FAILURE(status)) {
remove_proc_entry(BT_PROC_ROOT, bm_proc_root);
}
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: bt_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
bt_osl_cleanup (void)
{
bt_terminate();
if (bt_proc_root) {
remove_proc_entry(BT_PROC_ROOT, bm_proc_root);
}
return;
}
module_init(bt_osl_init);
module_exit(bt_osl_cleanup);
export-objs := bm_osl.o
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
/******************************************************************************
*
* Module Name: bm.c
* $Revision: 48 $
*
*****************************************************************************/
/*
* 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 <acpi.h>
#include "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bm")
/****************************************************************************
* Globals
****************************************************************************/
extern fadt_descriptor_rev2 acpi_fadt;
/* TBD: Make dynamically sizeable. */
BM_NODE_LIST node_list;
/****************************************************************************
* Internal Functions
****************************************************************************/
/*****************************************************************************
*
* FUNCTION: bm_print_object
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
bm_print_object (
acpi_handle handle)
{
acpi_buffer buffer;
acpi_handle parent;
acpi_object_type type;
buffer.length = 256;
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return;
}
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
acpi_get_parent(handle, &parent);
acpi_get_type(handle, &type);
/*
* TBD: Hack to get around scope identification problem.
*/
if (type == ACPI_TYPE_ANY) {
if (ACPI_SUCCESS(acpi_get_next_object(ACPI_TYPE_ANY,
handle, 0, NULL))) {
type = INTERNAL_TYPE_SCOPE;
}
}
switch (type)
{
case INTERNAL_TYPE_SCOPE:
acpi_os_printf("SCOPE: ");
break;
case ACPI_TYPE_INTEGER:
acpi_os_printf("SIMPLE (number): ");
break;
case ACPI_TYPE_STRING:
acpi_os_printf("SIMPLE (string): ");
break;
case ACPI_TYPE_BUFFER:
acpi_os_printf("SIMPLE (buffer): ");
break;
case ACPI_TYPE_PACKAGE:
acpi_os_printf("SIMPLE (package): ");
break;
case ACPI_TYPE_FIELD_UNIT:
acpi_os_printf("FIELD UNIT: ");
break;
case ACPI_TYPE_DEVICE:
acpi_os_printf("DEVICE: ");
break;
case ACPI_TYPE_EVENT:
acpi_os_printf("EVENT: ");
break;
case ACPI_TYPE_METHOD:
acpi_os_printf("CONTROL METHOD: ");
break;
case ACPI_TYPE_MUTEX:
acpi_os_printf("MUTEX: ");
break;
case ACPI_TYPE_REGION:
acpi_os_printf("OPERATION REGION: ");
break;
case ACPI_TYPE_POWER:
acpi_os_printf("POWER RESOURCE: ");
break;
case ACPI_TYPE_PROCESSOR:
acpi_os_printf("PROCESSOR: ");
break;
case ACPI_TYPE_THERMAL:
acpi_os_printf("THERMAL ZONE: ");
break;
case ACPI_TYPE_BUFFER_FIELD:
acpi_os_printf("BUFFER FIELD: ");
break;
case ACPI_TYPE_DDB_HANDLE:
acpi_os_printf("DDB HANDLE: ");
break;
default:
acpi_os_printf("OTHER (%d): ", type);
break;
}
acpi_os_printf("Object[%p][%s] parent[%p].\n", handle, (char*)buffer.pointer, parent);
acpi_os_free(buffer.pointer);
}
/****************************************************************************
*
* FUNCTION: bm_print_node
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
bm_print_node (
BM_NODE *node,
u32 flags)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
BM_DEVICE *device = NULL;
char *type_string = NULL;
PROC_NAME("bm_print_node");
if (!node) {
return;
}
device = &(node->device);
if (flags & BM_PRINT_PRESENT) {
if (!BM_DEVICE_PRESENT(device)) {
return;
}
}
buffer.length = 256;
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return;
}
acpi_get_name(device->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
switch(device->id.type) {
case BM_TYPE_SYSTEM:
type_string = " System";
break;
case BM_TYPE_SCOPE:
type_string = " Scope";
break;
case BM_TYPE_PROCESSOR:
type_string = " Proc";
break;
case BM_TYPE_THERMAL_ZONE:
type_string = "Thermal";
break;
case BM_TYPE_POWER_RESOURCE:
type_string = " Power";
break;
case BM_TYPE_FIXED_BUTTON:
type_string = " Button";
break;
case BM_TYPE_DEVICE:
type_string = " Device";
break;
default:
type_string = "Unknown";
break;
}
if (!(flags & BM_PRINT_GROUP)) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+-------------------------------------------------------------------------------\n"));
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| %s[%02x]:[%p] flags[%02x] hid[%s] %s\n", type_string, device->handle, device->acpi_handle, device->flags, (device->id.hid[0] ? device->id.hid : " "), (char*)buffer.pointer));
if (flags & BM_PRINT_IDENTIFICATION) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| identification: uid[%s] adr[%08x]\n", device->id.uid, device->id.adr));
}
if (flags & BM_PRINT_LINKAGE) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| linkage: this[%p] parent[%p] next[%p]\n", node, node->parent, node->next));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| scope.head[%p] scope.tail[%p]\n", node->scope.head, node->scope.tail));
}
if (flags & BM_PRINT_POWER) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| power: state[D%d] flags[%08x]\n", device->power.state, device->power.flags));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| S0[%02x] S1[%02x] S2[%02x] S3[%02x] S4[%02x] S5[%02x]\n", device->power.dx_supported[0], device->power.dx_supported[1], device->power.dx_supported[2], device->power.dx_supported[3], device->power.dx_supported[4], device->power.dx_supported[5]));
}
if (!(flags & BM_PRINT_GROUP)) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+-------------------------------------------------------------------------------\n"));
}
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: bm_print_hierarchy
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
bm_print_hierarchy (void)
{
#ifdef ACPI_DEBUG
u32 i = 0;
FUNCTION_TRACE("bm_print_hierarchy");
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
for (i = 0; i < node_list.count; i++) {
bm_print_node(node_list.nodes[i], BM_PRINT_GROUP | BM_PRINT_PRESENT);
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
#endif /*ACPI_DEBUG*/
return_VOID;
}
/****************************************************************************
*
* FUNCTION: bm_get_status
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_status (
BM_DEVICE *device)
{
acpi_status status = AE_OK;
if (!device) {
return AE_BAD_PARAMETER;
}
device->status = BM_STATUS_UNKNOWN;
/*
* Dynamic Status?
* ---------------
* If _STA isn't present we just return the default status.
*/
if (!(device->flags & BM_FLAGS_DYNAMIC_STATUS)) {
device->status = BM_STATUS_DEFAULT;
return AE_OK;
}
/*
* Evaluate _STA:
* --------------
*/
status = bm_evaluate_simple_integer(device->acpi_handle, "_STA",
&(device->status));
return status;
}
/****************************************************************************
*
* FUNCTION: bm_get_identification
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_identification (
BM_DEVICE *device)
{
acpi_status status = AE_OK;
acpi_device_info info;
if (!device) {
return AE_BAD_PARAMETER;
}
if (!(device->flags & BM_FLAGS_IDENTIFIABLE)) {
return AE_OK;
}
device->id.uid[0] = BM_UID_UNKNOWN;
device->id.hid[0] = BM_HID_UNKNOWN;
device->id.adr = BM_ADDRESS_UNKNOWN;
/*
* Get Object Info:
* ----------------
* Evalute _UID, _HID, and _ADR...
*/
status = acpi_get_object_info(device->acpi_handle, &info);
if (ACPI_FAILURE(status)) {
return status;
}
if (info.valid & ACPI_VALID_UID) {
MEMCPY((void*)device->id.uid, (void*)info.unique_id,
sizeof(BM_DEVICE_UID));
}
if (info.valid & ACPI_VALID_HID) {
MEMCPY((void*)device->id.hid, (void*)info.hardware_id,
sizeof(BM_DEVICE_HID));
}
if (info.valid & ACPI_VALID_ADR) {
device->id.adr = info.address;
}
return status;
}
/****************************************************************************
*
* FUNCTION: bm_get_flags
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_flags (
BM_DEVICE *device)
{
acpi_handle acpi_handle = NULL;
if (!device) {
return AE_BAD_PARAMETER;
}
device->flags = BM_FLAGS_UNKNOWN;
switch (device->id.type) {
case BM_TYPE_DEVICE:
/*
* Presence of _DCK indicates a docking station.
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_DCK", &acpi_handle))) {
device->flags |= BM_FLAGS_DOCKING_STATION;
}
/*
* Presence of _EJD and/or _EJx indicates 'ejectable'.
* TBD: _EJx...
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_EJD", &acpi_handle))) {
device->flags |= BM_FLAGS_EJECTABLE;
}
/*
* Presence of _PR0 or _PS0 indicates 'power manageable'.
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_PR0", &acpi_handle)) ||
ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_PS0", &acpi_handle))) {
device->flags |= BM_FLAGS_POWER_CONTROL;
}
/*
* Presence of _CRS indicates 'configurable'.
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_CRS", &acpi_handle))) {
device->flags |= BM_FLAGS_CONFIGURABLE;
}
/* Fall through to next case statement. */
case BM_TYPE_PROCESSOR:
case BM_TYPE_THERMAL_ZONE:
case BM_TYPE_POWER_RESOURCE:
/*
* Presence of _HID or _ADR indicates 'identifiable'.
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_HID", &acpi_handle)) ||
ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_ADR", &acpi_handle))) {
device->flags |= BM_FLAGS_IDENTIFIABLE;
}
/*
* Presence of _STA indicates 'dynamic status'.
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle,
"_STA", &acpi_handle))) {
device->flags |= BM_FLAGS_DYNAMIC_STATUS;
}
break;
}
return AE_OK;
}
/****************************************************************************
*
* FUNCTION: bm_add_namespace_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_add_namespace_device (
acpi_handle acpi_handle,
acpi_object_type acpi_type,
BM_NODE *parent,
BM_NODE **child)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
BM_DEVICE *device = NULL;
FUNCTION_TRACE("bm_add_namespace_device");
if (!parent || !child) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (node_list.count > BM_HANDLES_MAX) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
(*child) = NULL;
/*
* Create Node:
* ------------
*/
node = acpi_os_callocate(sizeof(BM_NODE));
if (!node) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
node->parent = parent;
node->next = NULL;
device = &(node->device);
device->handle = node_list.count;
device->acpi_handle = acpi_handle;
/*
* Device Type:
* ------------
*/
switch (acpi_type) {
case INTERNAL_TYPE_SCOPE:
device->id.type = BM_TYPE_SCOPE;
break;
case ACPI_TYPE_PROCESSOR:
device->id.type = BM_TYPE_PROCESSOR;
break;
case ACPI_TYPE_THERMAL:
device->id.type = BM_TYPE_THERMAL_ZONE;
break;
case ACPI_TYPE_POWER:
device->id.type = BM_TYPE_POWER_RESOURCE;
break;
case ACPI_TYPE_DEVICE:
device->id.type = BM_TYPE_DEVICE;
break;
}
/*
* Get Other Device Info:
* ----------------------
* But only if this device's parent is present (which implies
* this device MAY be present).
*/
if (BM_NODE_PRESENT(node->parent)) {
/*
* Device Flags
*/
status = bm_get_flags(device);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Device Identification
*/
status = bm_get_identification(device);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Device Status
*/
status = bm_get_status(device);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Power Management:
* -----------------
* If this node doesn't provide direct power control
* then we inherit PM capabilities from its parent.
*
* TBD: Inherit!
*/
if (BM_IS_POWER_CONTROL(device)) {
status = bm_get_pm_capabilities(node);
if (ACPI_FAILURE(status)) {
goto end;
}
}
}
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(node);
}
else {
/*
* Add to the node_list.
*/
node_list.nodes[node_list.count++] = node;
/*
* Formulate Hierarchy:
* --------------------
* Arrange within the namespace by assigning the parent and
* adding to the parent device's list of children (scope).
*/
if (!parent->scope.head) {
parent->scope.head = node;
}
else {
if (!parent->scope.tail) {
(parent->scope.head)->next = node;
}
else {
(parent->scope.tail)->next = node;
}
}
parent->scope.tail = node;
(*child) = node;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_enumerate_namespace
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_enumerate_namespace (void)
{
acpi_status status = AE_OK;
acpi_handle parent_handle = ACPI_ROOT_OBJECT;
acpi_handle child_handle = NULL;
BM_NODE *parent = NULL;
BM_NODE *child = NULL;
acpi_object_type acpi_type = 0;
u32 level = 1;
FUNCTION_TRACE("bm_enumerate_namespace");
parent = node_list.nodes[0];
/*
* Enumerate ACPI Namespace:
* -------------------------
* Parse through the ACPI namespace, identify all 'devices',
* and create a new entry for each in our collection.
*/
while (level > 0) {
/*
* Get the next object at this level.
*/
status = acpi_get_next_object(ACPI_TYPE_ANY, parent_handle, child_handle, &child_handle);
if (ACPI_SUCCESS(status)) {
/*
* TBD: This is a hack to get around the problem
* identifying scope objects. Scopes
* somehow need to be uniquely identified.
*/
status = acpi_get_type(child_handle, &acpi_type);
if (ACPI_SUCCESS(status) && (acpi_type == ACPI_TYPE_ANY)) {
status = acpi_get_next_object(ACPI_TYPE_ANY, child_handle, 0, NULL);
if (ACPI_SUCCESS(status)) {
acpi_type = INTERNAL_TYPE_SCOPE;
}
}
/*
* Device?
* -------
* If this object is a 'device', insert into the
* ACPI Bus Manager's local hierarchy and search
* the object's scope for any child devices (a
* depth-first search).
*/
switch (acpi_type) {
case INTERNAL_TYPE_SCOPE:
case ACPI_TYPE_DEVICE:
case ACPI_TYPE_PROCESSOR:
case ACPI_TYPE_THERMAL:
case ACPI_TYPE_POWER:
status = bm_add_namespace_device(child_handle, acpi_type, parent, &child);
if (ACPI_SUCCESS(status)) {
status = acpi_get_next_object(ACPI_TYPE_ANY, child_handle, 0, NULL);
if (ACPI_SUCCESS(status)) {
level++;
parent_handle = child_handle;
child_handle = 0;
parent = child;
}
}
break;
}
}
/*
* Scope Exhausted:
* ----------------
* No more children in this object's scope, Go back up
* in the namespace tree to the object's parent.
*/
else {
level--;
child_handle = parent_handle;
acpi_get_parent(parent_handle,
&parent_handle);
if (parent) {
parent = parent->parent;
}
else {
return_ACPI_STATUS(AE_NULL_ENTRY);
}
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_add_fixed_feature_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_add_fixed_feature_device (
BM_NODE *parent,
BM_DEVICE_TYPE device_type,
char *device_hid)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_add_fixed_feature_device");
if (!parent) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (node_list.count > BM_HANDLES_MAX) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/*
* Allocate the new device and add to the device array.
*/
node = acpi_os_callocate(sizeof(BM_NODE));
if (!node) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/*
* Get device info.
*/
node->device.handle = node_list.count;
node->device.acpi_handle = ACPI_ROOT_OBJECT;
node->device.id.type = BM_TYPE_FIXED_BUTTON;
if (device_hid) {
MEMCPY((void*)node->device.id.hid, device_hid,
sizeof(node->device.id.hid));
}
node->device.flags = BM_FLAGS_FIXED_FEATURE;
node->device.status = BM_STATUS_DEFAULT;
/* TBD: Device PM capabilities */
/*
* Add to the node_list.
*/
node_list.nodes[node_list.count++] = node;
/*
* Formulate Hierarchy:
* --------------------
* Arrange within the namespace by assigning the parent and
* adding to the parent device's list of children (scope).
*/
node->parent = parent;
node->next = NULL;
if (parent) {
if (!parent->scope.head) {
parent->scope.head = node;
}
else {
if (!parent->scope.tail) {
(parent->scope.head)->next = node;
}
else {
(parent->scope.tail)->next = node;
}
}
parent->scope.tail = node;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_enumerate_fixed_features
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_enumerate_fixed_features (void)
{
FUNCTION_TRACE("bm_enumerate_fixed_features");
/*
* Root Object:
* ------------
* Fabricate the root object, which happens to always get a
* device_handle of zero.
*/
node_list.nodes[0] = acpi_os_callocate(sizeof(BM_NODE));
if (NULL == (node_list.nodes[0])) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
node_list.nodes[0]->device.handle = BM_HANDLE_ROOT;
node_list.nodes[0]->device.acpi_handle = ACPI_ROOT_OBJECT;
node_list.nodes[0]->device.flags = BM_FLAGS_UNKNOWN;
node_list.nodes[0]->device.status = BM_STATUS_DEFAULT;
node_list.nodes[0]->device.id.type = BM_TYPE_SYSTEM;
/* TBD: Get system PM capabilities (Sx states?) */
node_list.count++;
/*
* Fixed Features:
* ---------------
* Enumerate fixed-feature devices (e.g. power and sleep buttons).
*/
if (acpi_fadt.pwr_button == 0) {
bm_add_fixed_feature_device(node_list.nodes[0],
BM_TYPE_FIXED_BUTTON, BM_HID_POWER_BUTTON);
}
if (acpi_fadt.sleep_button == 0) {
bm_add_fixed_feature_device(node_list.nodes[0],
BM_TYPE_FIXED_BUTTON, BM_HID_SLEEP_BUTTON);
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_get_handle
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_handle (
acpi_handle acpi_handle,
BM_HANDLE *device_handle)
{
acpi_status status = AE_NOT_FOUND;
u32 i = 0;
FUNCTION_TRACE("bm_get_handle");
if (!device_handle) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
*device_handle = BM_HANDLE_UNKNOWN;
/*
* Search all devices for a match on the ACPI handle.
*/
for (i=0; i<node_list.count; i++) {
if (!node_list.nodes[i]) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) node entry [%p] detected.\n", device_handle));
status = AE_NULL_ENTRY;
break;
}
if (node_list.nodes[i]->device.acpi_handle == acpi_handle) {
*device_handle = node_list.nodes[i]->device.handle;
status = AE_OK;
break;
}
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_get_node
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_node (
BM_HANDLE device_handle,
acpi_handle acpi_handle,
BM_NODE **node)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bm_get_node");
if (!node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/* busmgr failed to init, but we're being called by subordinate drivers */
if (node_list.count < 1) {
return_ACPI_STATUS(AE_NOT_FOUND);
}
/*
* If no device handle, resolve acpi handle to device handle.
*/
if (!device_handle && acpi_handle) {
status = bm_get_handle(acpi_handle, &device_handle);
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
}
/*
* Valid device handle?
*/
if (device_handle > BM_HANDLES_MAX) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid node handle [%02x] detected.\n", device_handle));
return_ACPI_STATUS(AE_ERROR);
}
*node = node_list.nodes[device_handle];
/*
* Valid node?
*/
if (!(*node)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) node entry [%02x] detected.\n", device_handle));
return_ACPI_STATUS(AE_NULL_ENTRY);
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_initialize
*
* PARAMETERS: <none>
*
* RETURN: Exception code.
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_initialize (void)
{
acpi_status status = AE_OK;
u32 start = 0;
u32 stop = 0;
u32 elapsed = 0;
FUNCTION_TRACE("bm_initialize");
MEMSET(&node_list, 0, sizeof(BM_NODE_LIST));
status = acpi_get_timer(&start);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Building device hierarchy.\n"));
/*
* Enumerate ACPI fixed-feature devices.
*/
status = bm_enumerate_fixed_features();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Enumerate the ACPI namespace.
*/
status = bm_enumerate_namespace();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
acpi_get_timer(&stop);
acpi_get_timer_duration(start, stop, &elapsed);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Building device hierarchy took [%d] microseconds.\n", elapsed));
/*
* Display hierarchy.
*/
bm_print_hierarchy();
/*
* Register for all standard and device-specific notifications.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Registering for all device notifications.\n"));
status = acpi_install_notify_handler(ACPI_ROOT_OBJECT,
ACPI_SYSTEM_NOTIFY, &bm_notify, NULL);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to register for standard notifications.\n"));
return_ACPI_STATUS(status);
}
status = acpi_install_notify_handler(ACPI_ROOT_OBJECT,
ACPI_DEVICE_NOTIFY, &bm_notify, NULL);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to register for device-specific notifications.\n"));
return_ACPI_STATUS(status);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Bus Manager enabled.\n"));
/*
* Initialize built-in power resource driver.
*/
bm_pr_initialize();
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_terminate
*
* PARAMETERS: <none>
*
* RETURN: Exception code.
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_terminate (void)
{
acpi_status status = AE_OK;
u32 i = 0;
FUNCTION_TRACE("bm_terminate");
/*
* Terminate built-in power resource driver.
*/
bm_pr_terminate();
/*
* Unregister for all notifications.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Unregistering for device notifications.\n"));
status = acpi_remove_notify_handler(ACPI_ROOT_OBJECT,
ACPI_SYSTEM_NOTIFY, &bm_notify);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to un-register for standard notifications.\n"));
}
status = acpi_remove_notify_handler(ACPI_ROOT_OBJECT,
ACPI_DEVICE_NOTIFY, &bm_notify);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to un-register for device-specific notifications.\n"));
}
/*
* Parse through the device array, freeing all entries.
*/
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing device hierarchy.\n"));
for (i = 0; i < node_list.count; i++) {
if (node_list.nodes[i]) {
acpi_os_free(node_list.nodes[i]);
}
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Bus Manager disabled.\n"));
return_ACPI_STATUS(AE_OK);
}
/*****************************************************************************
*
* Module Name: bm_osl.c
* $Revision: 17 $
*
*****************************************************************************/
/*
* 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/spinlock.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <acpi.h>
#include "bm.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - ACPI Bus Manager");
MODULE_LICENSE("GPL");
/*****************************************************************************
* Types & Defines
*****************************************************************************/
typedef struct
{
BM_HANDLE device_handle;
char *device_type;
char *device_instance;
u32 event_type;
u32 event_data;
struct list_head list;
} BM_OSL_EVENT;
#define BM_PROC_ROOT "acpi"
#define BM_PROC_EVENT "event"
#define BM_PROC_DEVICES "devices"
#define BM_MAX_STRING_LENGTH 80
/****************************************************************************
* Globals
****************************************************************************/
struct proc_dir_entry *bm_proc_root = NULL;
static struct proc_dir_entry *bm_proc_event = NULL;
#ifdef ACPI_DEBUG
static u32 save_dbg_layer;
static u32 save_dbg_level;
#endif /*ACPI_DEBUG*/
extern BM_NODE_LIST node_list;
static spinlock_t bm_osl_event_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(bm_event_list);
static DECLARE_WAIT_QUEUE_HEAD(bm_event_wait_queue);
static int event_is_open = 0;
/****************************************************************************
* Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_osl_generate_event
*
* DESCRIPTION: Generates an event for user-space consumption by writing
* the event data to the 'event' file.
*
****************************************************************************/
acpi_status
bm_osl_generate_event (
BM_HANDLE device_handle,
char *device_type,
char *device_instance,
u32 event_type,
u32 event_data)
{
BM_OSL_EVENT *event = NULL;
u32 flags = 0;
/* drop event on the floor if no one's listening */
if (!event_is_open)
return (AE_OK);
/*
* Allocate a new event structure.
*/
event = acpi_os_callocate(sizeof(BM_OSL_EVENT));
if (!event)
goto alloc_error;
event->device_type = acpi_os_callocate(strlen(device_type)
+ sizeof(char));
if (!event->device_type)
goto alloc_error;
event->device_instance = acpi_os_callocate(strlen(device_instance)
+ sizeof(char));
if (!event->device_instance)
goto alloc_error;
/*
* Set event data.
*/
event->device_handle = device_handle;
strcpy(event->device_type, device_type);
strcpy(event->device_instance, device_instance);
event->event_type = event_type;
event->event_data = event_data;
/*
* Add to the end of our event list.
*/
spin_lock_irqsave(&bm_osl_event_lock, flags);
list_add_tail(&event->list, &bm_event_list);
spin_unlock_irqrestore(&bm_osl_event_lock, flags);
/*
* Signal waiting threads (if any).
*/
wake_up_interruptible(&bm_event_wait_queue);
return(AE_OK);
alloc_error:
if (event->device_instance)
acpi_os_free(event->device_instance);
if (event->device_type)
acpi_os_free(event->device_type);
if (event)
acpi_os_free(event);
return (AE_NO_MEMORY);
}
static int bm_osl_open_event(struct inode *inode, struct file *file)
{
spin_lock_irq (&bm_osl_event_lock);
if(event_is_open)
goto out_busy;
event_is_open = 1;
spin_unlock_irq (&bm_osl_event_lock);
return 0;
out_busy:
spin_unlock_irq (&bm_osl_event_lock);
return -EBUSY;
}
static int bm_osl_close_event(struct inode *inode, struct file *file)
{
event_is_open = 0;
return 0;
}
/****************************************************************************
*
* FUNCTION: bm_osl_read_event
*
* DESCRIPTION: Handles reads to the 'event' file by blocking user-mode
* threads until data (an event) is generated.
*
****************************************************************************/
static ssize_t
bm_osl_read_event(
struct file *file,
char *buf,
size_t count,
loff_t *ppos)
{
BM_OSL_EVENT *event = NULL;
unsigned long flags = 0;
static char str[BM_MAX_STRING_LENGTH];
static int chars_remaining = 0;
static char *ptr;
if (!chars_remaining) {
DECLARE_WAITQUEUE(wait, current);
if (list_empty(&bm_event_list)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&bm_event_wait_queue, &wait);
if (list_empty(&bm_event_list)) {
schedule();
}
remove_wait_queue(&bm_event_wait_queue, &wait);
set_current_state(TASK_RUNNING);
if (signal_pending(current)) {
return -ERESTARTSYS;
}
}
spin_lock_irqsave(&bm_osl_event_lock, flags);
event = list_entry(bm_event_list.next, BM_OSL_EVENT, list);
list_del(&event->list);
spin_unlock_irqrestore(&bm_osl_event_lock, flags);
chars_remaining = sprintf(str, "%s %s %08x %08x\n",
event->device_type, event->device_instance,
event->event_type, event->event_data);
ptr = str;
acpi_os_free(event->device_type);
acpi_os_free(event->device_instance);
acpi_os_free(event);
}
if (chars_remaining < count)
count = chars_remaining;
if (copy_to_user(buf, ptr, count))
return -EFAULT;
*ppos += count;
chars_remaining -= count;
ptr += count;
return count;
}
/****************************************************************************
*
* FUNCTION: bm_osl_poll_event
*
* DESCRIPTION: Handles poll() of the 'event' file by blocking user-mode
* threads until data (an event) is generated.
*
****************************************************************************/
static unsigned int
bm_osl_poll_event(
struct file *file,
poll_table *wait)
{
poll_wait(file, &bm_event_wait_queue, wait);
if (!list_empty(&bm_event_list))
return POLLIN | POLLRDNORM;
return 0;
}
struct file_operations proc_event_operations = {
open: bm_osl_open_event,
read: bm_osl_read_event,
release: bm_osl_close_event,
poll: bm_osl_poll_event,
};
/****************************************************************************
*
* FUNCTION: bm_osl_init
*
****************************************************************************/
int
bm_osl_init(void)
{
acpi_status status = AE_OK;
status = acpi_subsystem_status();
if (ACPI_FAILURE(status))
return -ENODEV;
bm_proc_root = proc_mkdir(BM_PROC_ROOT, NULL);
if (!bm_proc_root) {
return(AE_ERROR);
}
bm_proc_event = create_proc_entry(BM_PROC_EVENT, S_IRUSR, bm_proc_root);
if (bm_proc_event) {
bm_proc_event->proc_fops = &proc_event_operations;
}
status = bm_initialize();
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: bm_osl_cleanup
*
****************************************************************************/
void
bm_osl_cleanup(void)
{
bm_terminate();
if (bm_proc_event) {
remove_proc_entry(BM_PROC_EVENT, bm_proc_root);
bm_proc_event = NULL;
}
if (bm_proc_root) {
remove_proc_entry(BM_PROC_ROOT, NULL);
bm_proc_root = NULL;
}
return;
}
module_init(bm_osl_init);
module_exit(bm_osl_cleanup);
/****************************************************************************
* Symbols
****************************************************************************/
/* bm.c */
EXPORT_SYMBOL(bm_get_node);
/* bmdriver.c */
EXPORT_SYMBOL(bm_get_device_power_state);
EXPORT_SYMBOL(bm_set_device_power_state);
EXPORT_SYMBOL(bm_get_device_info);
EXPORT_SYMBOL(bm_get_device_status);
EXPORT_SYMBOL(bm_get_device_context);
EXPORT_SYMBOL(bm_register_driver);
EXPORT_SYMBOL(bm_unregister_driver);
/* bmsearch.c */
EXPORT_SYMBOL(bm_search);
/* bmrequest.c */
EXPORT_SYMBOL(bm_request);
/* bmutils.c */
EXPORT_SYMBOL(bm_extract_package_data);
EXPORT_SYMBOL(bm_evaluate_object);
EXPORT_SYMBOL(bm_evaluate_simple_integer);
EXPORT_SYMBOL(bm_evaluate_reference_list);
EXPORT_SYMBOL(bm_copy_to_buffer);
EXPORT_SYMBOL(bm_cast_buffer);
/* bm_proc.c */
EXPORT_SYMBOL(bm_osl_generate_event);
EXPORT_SYMBOL(bm_proc_root);
/*****************************************************************************
*
* Module Name: bmdriver.c
* $Revision: 21 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmdriver")
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_get_device_power_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_device_power_state (
BM_HANDLE device_handle,
BM_POWER_STATE *state)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_get_device_power_state");
if (!state) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
*state = ACPI_STATE_UNKNOWN;
/*
* Resolve device handle to node.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Get the current power state.
*/
status = bm_get_power_state(node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
*state = node->device.power.state;
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_set_device_power_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_set_device_power_state (
BM_HANDLE device_handle,
BM_POWER_STATE state)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_set_device_power_state");
/*
* Resolve device handle to node.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Set the current power state.
*/
status = bm_set_power_state(node, state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_get_device_status
*
* PARAMETERS:
* device_handle is really an index number into the array of BM_DEVICE
* structures in info_list. This data item is passed to
* the registered program's "notify" callback. It is used
* to retrieve the specific BM_DEVICE structure instance
* associated with the callback.
* device_status is a pointer that receives the result of processing
* the device's associated ACPI _STA.
*
* RETURN:
* The acpi_status value indicates success AE_OK or failure of the function
*
* DESCRIPTION: Evaluates the device's ACPI _STA, if it is present.
*
****************************************************************************/
acpi_status
bm_get_device_status (
BM_HANDLE device_handle,
BM_DEVICE_STATUS *device_status)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_get_device_status");
if (!device_status) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
*device_status = BM_STATUS_UNKNOWN;
/*
* Resolve device handle to node.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Parent Present?
* ---------------
* If the parent isn't present we can't evalute _STA on the child.
* Return an unknown status.
*/
if (!BM_NODE_PRESENT(node->parent)) {
return_ACPI_STATUS(AE_OK);
}
/*
* Dynamic Status?
* ---------------
* If _STA isn't present we just return the default status.
*/
if (!(node->device.flags & BM_FLAGS_DYNAMIC_STATUS)) {
*device_status = BM_STATUS_DEFAULT;
return_ACPI_STATUS(AE_OK);
}
/*
* Evaluate _STA:
* --------------
*/
status = bm_evaluate_simple_integer(node->device.acpi_handle, "_STA",
&(node->device.status));
if (ACPI_SUCCESS(status)) {
*device_status = node->device.status;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_get_device_info
*
* PARAMETERS:
* device_handle An index used to retrieve the associated BM_DEVICE info.
* device A pointer to a BM_DEVICE structure instance pointer.
* This pointed to BM_DEVICE structure will contain the
* this device's information.
*
* RETURN:
* The acpi_status value indicates success AE_OK or failure of the function
*
* DESCRIPTION:
* Using the device_handle this function retrieves this device's
* BM_DEVICE structure instance and save's it in device.
*
****************************************************************************/
acpi_status
bm_get_device_info (
BM_HANDLE device_handle,
BM_DEVICE **device)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_get_device_info");
if (!device) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Resolve device handle to internal device.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
*device = &(node->device);
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_get_device_context
*
* device_handle An index used to retrieve the associated BM_DEVICE info.
* context A pointer to a BM_DRIVER_CONTEXT structure instance.
*
* RETURN:
* The acpi_status value indicates success AE_OK or failure of the function
*
* DESCRIPTION:
* Using the device_handle this function retrieves this device's
* BM_DRIVER_CONTEXT structure instance and save's it in context.
*
****************************************************************************/
acpi_status
bm_get_device_context (
BM_HANDLE device_handle,
BM_DRIVER_CONTEXT *context)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_get_device_context");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
*context = NULL;
/*
* Resolve device handle to internal device.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
if (!node->driver.context) {
return_ACPI_STATUS(AE_NULL_ENTRY);
}
*context = node->driver.context;
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_register_driver
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_register_driver (
BM_DEVICE_ID *criteria,
BM_DRIVER *driver)
{
acpi_status status = AE_NOT_FOUND;
BM_HANDLE_LIST device_list;
BM_NODE *node = NULL;
BM_DEVICE *device = NULL;
u32 i = 0;
FUNCTION_TRACE("bm_register_driver");
if (!criteria || !driver || !driver->notify || !driver->request) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&device_list, 0, sizeof(BM_HANDLE_LIST));
/*
* Find Matches:
* -------------
* Search through the entire device hierarchy for matches against
* the given device criteria.
*/
status = bm_search(BM_HANDLE_ROOT, criteria, &device_list);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Install driver:
* ----------------
* For each match, record the driver information and execute the
* driver's Notify() funciton (if present) to notify the driver
* of the device's presence.
*/
for (i = 0; i < device_list.count; i++) {
/* Resolve the device handle. */
status = bm_get_node(device_list.handles[i], 0, &node);
if (ACPI_FAILURE(status)) {
continue;
}
device = &(node->device);
/*
* Make sure another driver hasn't already registered for
* this device.
*/
if (BM_IS_DRIVER_CONTROL(device)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Another driver has already registered for device [%02x].\n", device->handle));
continue;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Registering driver for device [%02x].\n", device->handle));
/* Notify driver of new device. */
status = driver->notify(BM_NOTIFY_DEVICE_ADDED,
node->device.handle, &(node->driver.context));
if (ACPI_SUCCESS(status)) {
node->driver.notify = driver->notify;
node->driver.request = driver->request;
node->device.flags |= BM_FLAGS_DRIVER_CONTROL;
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_unregister_driver
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_unregister_driver (
BM_DEVICE_ID *criteria,
BM_DRIVER *driver)
{
acpi_status status = AE_NOT_FOUND;
BM_HANDLE_LIST device_list;
BM_NODE *node = NULL;
BM_DEVICE *device = NULL;
u32 i = 0;
FUNCTION_TRACE("bm_unregister_driver");
if (!criteria || !driver || !driver->notify || !driver->request) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&device_list, 0, sizeof(BM_HANDLE_LIST));
/*
* Find Matches:
* -------------
* Search through the entire device hierarchy for matches against
* the given device criteria.
*/
status = bm_search(BM_HANDLE_ROOT, criteria, &device_list);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Remove driver:
* ---------------
* For each match, execute the driver's Notify() function to allow
* the driver to cleanup each device instance.
*/
for (i = 0; i < device_list.count; i++) {
/* Resolve the device handle. */
status = bm_get_node(device_list.handles[i], 0, &node);
if (ACPI_FAILURE(status)) {
continue;
}
device = &(node->device);
/*
* Make sure driver has really registered for this device.
*/
if (!BM_IS_DRIVER_CONTROL(device)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Driver hasn't registered for device [%02x].\n", device->handle));
continue;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Unregistering driver for device [%02x].\n", device->handle));
/* Notify driver of device removal. */
status = node->driver.notify(BM_NOTIFY_DEVICE_REMOVED,
node->device.handle, &(node->driver.context));
if (ACPI_SUCCESS(status)) {
node->driver.notify = NULL;
node->driver.request = NULL;
node->driver.context = NULL;
node->device.flags &= ~BM_FLAGS_DRIVER_CONTROL;
}
}
return_ACPI_STATUS(AE_OK);
}
/*****************************************************************************
*
* Module Name: bmnotify.c
* $Revision: 21 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmnotify")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_generate_notify
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_generate_notify (
BM_NODE *node,
u32 notify_type)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
FUNCTION_TRACE("bm_generate_notify");
if (!node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
device = &(node->device);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Sending notify [%02x] to device [%02x].\n", notify_type, node->device.handle));
if (!BM_IS_DRIVER_CONTROL(device)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "No driver installed for device [%02x].\n", device->handle));
return_ACPI_STATUS(AE_NOT_EXIST);
}
status = node->driver.notify(notify_type, node->device.handle,
&(node->driver.context));
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_device_check
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_device_check (
BM_NODE *node,
u32 *status_change)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
BM_DEVICE_STATUS old_status = BM_STATUS_UNKNOWN;
FUNCTION_TRACE("bm_device_check");
if (!node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
device = &(node->device);
if (status_change) {
*status_change = FALSE;
}
old_status = device->status;
/*
* Parent Present?
* ---------------
* Only check this device if its parent is present (which implies
* this device MAY be present).
*/
if (!BM_NODE_PRESENT(node->parent)) {
return_ACPI_STATUS(AE_OK);
}
/*
* Get Status:
* -----------
* And see if the status has changed.
*/
status = bm_get_status(device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
if (old_status == node->device.status) {
return_ACPI_STATUS(AE_OK);
}
if (status_change) {
*status_change = TRUE;
}
/*
* Device Insertion?
* -----------------
*/
if ((device->status & BM_STATUS_PRESENT) &&
!(old_status & BM_STATUS_PRESENT)) {
/* TBD: Make sure driver is loaded, and if not, load. */
status = bm_generate_notify(node, BM_NOTIFY_DEVICE_ADDED);
}
/*
* Device Removal?
* ---------------
*/
else if (!(device->status & BM_STATUS_PRESENT) &&
(old_status & BM_STATUS_PRESENT)) {
/* TBD: Unload driver if last device instance. */
status = bm_generate_notify(node, BM_NOTIFY_DEVICE_REMOVED);
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_bus_check
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_bus_check (
BM_NODE *parent_node)
{
acpi_status status = AE_OK;
u32 status_change = FALSE;
FUNCTION_TRACE("bm_bus_check");
if (!parent_node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Status Change?
* --------------
*/
status = bm_device_check(parent_node, &status_change);
if (ACPI_FAILURE(status) || !status_change) {
return_ACPI_STATUS(status);
}
/*
* Enumerate Scope:
* ----------------
* TBD: Enumerate child devices within this device's scope and
* run bm_device_check()'s on them...
*/
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_notify
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
bm_notify (
acpi_handle acpi_handle,
u32 notify_value,
void *context)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_notify");
/*
* Resolve the ACPI handle.
*/
status = bm_get_node(0, acpi_handle, &node);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Recieved notify [%02x] for unknown device [%p].\n", notify_value, acpi_handle));
return_VOID;
}
/*
* Device-Specific or Standard?
* ----------------------------
* Device-specific notifies are forwarded to the control module's
* notify() function for processing. Standard notifies are handled
* internally.
*/
if (notify_value > 0x7F) {
status = bm_generate_notify(node, notify_value);
}
else {
switch (notify_value) {
case BM_NOTIFY_BUS_CHECK:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received BUS CHECK notification for device [%02x].\n", node->device.handle));
status = bm_bus_check(node);
break;
case BM_NOTIFY_DEVICE_CHECK:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received DEVICE CHECK notification for device [%02x].\n", node->device.handle));
status = bm_device_check(node, NULL);
break;
case BM_NOTIFY_DEVICE_WAKE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received DEVICE WAKE notification for device [%02x].\n", node->device.handle));
/* TBD */
break;
case BM_NOTIFY_EJECT_REQUEST:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received EJECT REQUEST notification for device [%02x].\n", node->device.handle));
/* TBD */
break;
case BM_NOTIFY_DEVICE_CHECK_LIGHT:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received DEVICE CHECK LIGHT notification for device [%02x].\n", node->device.handle));
/* TBD: Exactly what does the 'light' mean? */
status = bm_device_check(node, NULL);
break;
case BM_NOTIFY_FREQUENCY_MISMATCH:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received FREQUENCY MISMATCH notification for device [%02x].\n", node->device.handle));
/* TBD */
break;
case BM_NOTIFY_BUS_MODE_MISMATCH:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received BUS MODE MISMATCH notification for device [%02x].\n", node->device.handle));
/* TBD */
break;
case BM_NOTIFY_POWER_FAULT:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received POWER FAULT notification.\n"));
/* TBD */
break;
default:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received unknown/unsupported notification.\n"));
break;
}
}
return_VOID;
}
/*****************************************************************************
*
* Module Name: bmpm.c
* $Revision: 14 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#include "bmpower.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmpm")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_get_inferred_power_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_inferred_power_state (
BM_DEVICE *device)
{
acpi_status status = AE_OK;
BM_HANDLE_LIST pr_list;
BM_POWER_STATE list_state = ACPI_STATE_UNKNOWN;
char object_name[5] = {'_','P','R','0','\0'};
u32 i = 0;
FUNCTION_TRACE("bm_get_inferred_power_state");
if (!device) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&pr_list, 0, sizeof(BM_HANDLE_LIST));
device->power.state = ACPI_STATE_D3;
/*
* Calculate Power State:
* ----------------------
* Try to infer the devices's power state by checking the state of
* the devices's power resources. We start by evaluating _PR0
* (resource requirements at D0) and work through _PR1 and _PR2.
* We know the current devices power state when all resources (for
* a give Dx state) are ON. If no power resources are on then the
* device is assumed to be off (D3).
*/
for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
object_name[3] = '0' + i;
status = bm_evaluate_reference_list(device->acpi_handle,
object_name, &pr_list);
if (ACPI_SUCCESS(status)) {
status = bm_pr_list_get_state(&pr_list, &list_state);
if (ACPI_SUCCESS(status)) {
if (list_state == ACPI_STATE_D0) {
device->power.state = i;
break;
}
}
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_get_power_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_power_state (
BM_NODE *node)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
FUNCTION_TRACE("bm_get_power_state");
if (!node || !node->parent) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
device = &(node->device);
device->power.state = ACPI_STATE_UNKNOWN;
/*
* Power Control?
* --------------
* If this device isn't directly power manageable (e.g. doesn't
* include _PR0/_PS0) then there's nothing to do (state is static).
*/
if (!BM_IS_POWER_CONTROL(device)) {
return_ACPI_STATUS(AE_OK);
}
/*
* Parent Present?
* ---------------
* Make sure the parent is present before mucking with the child.
*/
if (!BM_NODE_PRESENT(node->parent)) {
return_ACPI_STATUS(AE_NOT_EXIST);
}
/*
* Get Power State:
* ----------------
* Either directly (via _PSC) or inferred (via power resource
* dependencies).
*/
if (BM_IS_POWER_STATE(device)) {
status = bm_evaluate_simple_integer(device->acpi_handle,
"_PSC", &(device->power.state));
}
else {
status = bm_get_inferred_power_state(device);
}
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device [%02x] is at power state [D%d].\n", device->handle, device->power.state));
}
else {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Error getting power state for device [%02x]\n", device->handle));
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_set_power_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_set_power_state (
BM_NODE *node,
BM_POWER_STATE state)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
BM_DEVICE *parent_device = NULL;
BM_HANDLE_LIST current_list;
BM_HANDLE_LIST target_list;
char object_name[5] = {'_','P','R','0','\0'};
FUNCTION_TRACE("bm_set_power_state");
if (!node || !node->parent || (state > ACPI_STATE_D3)) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&current_list, 0, sizeof(BM_HANDLE_LIST));
MEMSET(&target_list, 0, sizeof(BM_HANDLE_LIST));
device = &(node->device);
parent_device = &(node->parent->device);
/*
* Power Control?
* --------------
* If this device isn't directly power manageable (e.g. doesn't
* include _PR0/_PS0) then return an error (can't set state).
*/
if (!BM_IS_POWER_CONTROL(device)) {
return_ACPI_STATUS(AE_ERROR);
}
/*
* Parent Present?
* ---------------
* Make sure the parent is present before mucking with the child.
*/
if (!BM_NODE_PRESENT(node->parent)) {
return_ACPI_STATUS(AE_NOT_EXIST);
}
/*
* Check Parent's Power State:
* ---------------------------
* Can't be in a higher power state (lower Dx value) than parent.
*/
if (state < parent_device->power.state) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Cannot set device [%02x] to a higher-powered state than parent_device.\n", device->handle));
return_ACPI_STATUS(AE_ERROR);
}
/*
* Get Resources:
* --------------
* Get the power resources associated with the device's current
* and target power states.
*/
if (device->power.state != ACPI_STATE_UNKNOWN) {
object_name[3] = '0' + device->power.state;
bm_evaluate_reference_list(device->acpi_handle,
object_name, &current_list);
}
object_name[3] = '0' + state;
bm_evaluate_reference_list(device->acpi_handle, object_name,
&target_list);
/*
* Transition Resources:
* ---------------------
* Transition all power resources referenced by this device to
* the correct power state (taking into consideration sequencing
* and dependencies to other devices).
*/
if (current_list.count || target_list.count) {
status = bm_pr_list_transition(&current_list, &target_list);
}
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Execute _PSx:
* -------------
* Execute the _PSx method corresponding to the target Dx state,
* if it exists.
*/
object_name[2] = 'S';
object_name[3] = '0' + state;
bm_evaluate_object(device->acpi_handle, object_name, NULL, NULL);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device [%02x] is now at [D%d].\n", device->handle, state));
device->power.state = state;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_get_pm_capabilities
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_get_pm_capabilities (
BM_NODE *node)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
BM_DEVICE *parent_device = NULL;
acpi_handle acpi_handle = NULL;
BM_POWER_STATE dx_supported = ACPI_STATE_UNKNOWN;
char object_name[5] = {'_','S','0','D','\0'};
u32 i = 0;
FUNCTION_TRACE("bm_get_pm_capabilities");
if (!node || !node->parent) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
device = &(node->device);
parent_device = &(node->parent->device);
/*
* Power Management Flags:
* -----------------------
*/
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PSC",
&acpi_handle))) {
device->power.flags |= BM_FLAGS_POWER_STATE;
}
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_IRC",
&acpi_handle))) {
device->power.flags |= BM_FLAGS_INRUSH_CURRENT;
}
if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PRW",
&acpi_handle))) {
device->power.flags |= BM_FLAGS_WAKE_CAPABLE;
}
/*
* Device Power State:
* -------------------
* Note that we can't get the device's power state until we've
* initialized all power resources, so for now we just set to
* unknown.
*/
device->power.state = ACPI_STATE_UNKNOWN;
/*
* Dx Supported in S0:
* -------------------
* Figure out which Dx states are supported by this device for the
* S0 (working) state. Note that D0 and D3 are required (assumed).
*/
device->power.dx_supported[ACPI_STATE_S0] = BM_FLAGS_D0_SUPPORT |
BM_FLAGS_D3_SUPPORT;
if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR1",
&acpi_handle))) ||
(ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS1",
&acpi_handle)))) {
device->power.dx_supported[ACPI_STATE_S0] |=
BM_FLAGS_D1_SUPPORT;
}
if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR2",
&acpi_handle))) ||
(ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS2",
&acpi_handle)))) {
device->power.dx_supported[ACPI_STATE_S0] |=
BM_FLAGS_D2_SUPPORT;
}
/*
* Dx Supported in S1-S5:
* ----------------------
* Figure out which Dx states are supported by this device for
* all other Sx states.
*/
for (i = ACPI_STATE_S1; i <= ACPI_STATE_S5; i++) {
/*
* D3 support is assumed (off is always possible!).
*/
device->power.dx_supported[i] = BM_FLAGS_D3_SUPPORT;
/*
* Evalute _Sx_d:
* -------------
* Which returns the highest (power) Dx state supported in
* this system (Sx) state. We convert this value to a bit
* mask of supported states (conceptually simpler).
*/
status = bm_evaluate_simple_integer(device->acpi_handle,
object_name, &dx_supported);
if (ACPI_SUCCESS(status)) {
switch (dx_supported) {
case 0:
device->power.dx_supported[i] |=
BM_FLAGS_D0_SUPPORT;
/* fall through */
case 1:
device->power.dx_supported[i] |=
BM_FLAGS_D1_SUPPORT;
/* fall through */
case 2:
device->power.dx_supported[i] |=
BM_FLAGS_D2_SUPPORT;
/* fall through */
case 3:
device->power.dx_supported[i] |=
BM_FLAGS_D3_SUPPORT;
break;
}
/*
* Validate:
* ---------
* Mask of any states that _Sx_d falsely advertises
* (e.g.claims D1 support but neither _PR2 or _PS2
* exist). In other words, S1-S5 can't offer a Dx
* state that isn't supported by S0.
*/
device->power.dx_supported[i] &=
device->power.dx_supported[ACPI_STATE_S0];
}
object_name[2]++;
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* Module Name: bmpower.c - Driver for ACPI Power Resource 'devices'
* $Revision: 20 $
*
****************************************************************************/
/*
* 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
*/
/*
* TBD: 1. Sequencing of power resource list transitions.
* 2. Global serialization of power resource transtions (see ACPI
* spec section 7.1.2/7.1.3).
* 3. Better error handling.
*/
#include <acpi.h>
#include "bm.h"
#include "bmpower.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmpower")
/****************************************************************************
* Function Prototypes
****************************************************************************/
acpi_status
bm_pr_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context);
acpi_status
bm_pr_request (
BM_REQUEST *request,
void *context);
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_pr_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_print (
BM_POWER_RESOURCE *pr)
{
acpi_buffer buffer;
PROC_NAME("bm_pr_print");
if (!pr) {
return(AE_BAD_PARAMETER);
}
buffer.length = 256;
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return(AE_NO_MEMORY);
}
acpi_get_name(pr->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
acpi_os_printf("Power Resource: found\n");
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Power_resource[%02x]:[%p] %s\n", pr->device_handle, pr->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| system_level[S%d] resource_order[%d]\n", pr->system_level, pr->resource_order));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| state[D%d] reference_count[%d]\n", pr->state, pr->reference_count));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_pr_get_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_get_state (
BM_POWER_RESOURCE *pr)
{
acpi_status status = AE_OK;
BM_DEVICE_STATUS device_status = BM_STATUS_UNKNOWN;
FUNCTION_TRACE("bm_pr_get_state");
if (!pr) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
pr->state = ACPI_STATE_UNKNOWN;
/*
* Evaluate _STA:
* --------------
* Evalute _STA to determine whether the power resource is ON or OFF.
* Note that if the power resource isn't present we'll get AE_OK but
* an unknown status.
*/
status = bm_get_device_status(pr->device_handle, &device_status);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Error reading status for power resource [%02x].\n", pr->device_handle));
return_ACPI_STATUS(status);
}
/*
* Mask off all bits but the first as some systems return non-standard
* values (e.g. 0x51).
*/
switch (device_status & 0x01) {
case 0:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Power resource [%02x] is OFF.\n", pr->device_handle));
pr->state = ACPI_STATE_D3;
break;
case 1:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Power resource [%02x] is ON.\n", pr->device_handle));
pr->state = ACPI_STATE_D0;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_set_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_set_state (
BM_POWER_RESOURCE *pr,
BM_POWER_STATE target_state)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bm_pr_set_state");
if (!pr) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
status = bm_pr_get_state(pr);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
if (target_state == pr->state) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Power resource [%02x] already at target power state [D%d].\n", pr->device_handle, pr->state));
return_ACPI_STATUS(AE_OK);
}
switch (target_state) {
case ACPI_STATE_D0:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Turning power resource [%02x] ON.\n", pr->device_handle));
status = bm_evaluate_object(pr->acpi_handle, "_ON", NULL, NULL);
break;
case ACPI_STATE_D3:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Turning power resource [%02x] OFF.\n", pr->device_handle));
status = bm_evaluate_object(pr->acpi_handle, "_OFF", NULL, NULL);
break;
default:
status = AE_BAD_PARAMETER;
break;
}
status = bm_pr_get_state(pr);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_list_get_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_list_get_state (
BM_HANDLE_LIST *pr_list,
BM_POWER_STATE *power_state)
{
acpi_status status = AE_OK;
BM_POWER_RESOURCE *pr = NULL;
u32 i = 0;
FUNCTION_TRACE("bm_pr_list_get_state");
if (!pr_list || !power_state) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (pr_list->count < 1) {
pr->state = ACPI_STATE_UNKNOWN;
return_ACPI_STATUS(AE_ERROR);
}
(*power_state) = ACPI_STATE_D0;
/*
* Calculate Current power_state:
* -----------------------------
* The current state of a list of power resources is ON if all
* power resources are currently in the ON state. In other words,
* if any power resource in the list is OFF then the collection
* isn't fully ON.
*/
for (i = 0; i < pr_list->count; i++) {
status = bm_get_device_context(pr_list->handles[i],
(BM_DRIVER_CONTEXT*)(&pr));
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Invalid reference to power resource [%02x].\n", pr_list->handles[i]));
(*power_state) = ACPI_STATE_UNKNOWN;
break;
}
status = bm_pr_get_state(pr);
if (ACPI_FAILURE(status)) {
(*power_state) = ACPI_STATE_UNKNOWN;
break;
}
if (pr->state != ACPI_STATE_D0) {
(*power_state) = pr->state;
break;
}
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_list_transition
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_list_transition (
BM_HANDLE_LIST *current_list,
BM_HANDLE_LIST *target_list)
{
acpi_status status = AE_OK;
BM_POWER_RESOURCE *pr = NULL;
u32 i = 0;
FUNCTION_TRACE("bm_pr_list_transition");
if (!current_list || !target_list) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Reference Target:
* -----------------
* Reference all resources for the target power state first (so
* the device doesn't get turned off while transitioning). Power
* resources that aren't on (new reference count of 1) are turned on.
*/
for (i = 0; i < target_list->count; i++) {
status = bm_get_device_context(target_list->handles[i],
(BM_DRIVER_CONTEXT*)(&pr));
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Invalid reference to power resource [%02x].\n", target_list->handles[i]));
continue;
}
if (++pr->reference_count == 1) {
/* TBD: Need ordering based upon resource_order */
status = bm_pr_set_state(pr, ACPI_STATE_D0);
if (ACPI_FAILURE(status)) {
/* TBD: How do we handle this? */
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to change power state for power resource [%02x].\n", target_list->handles[i]));
}
}
}
/*
* Dereference Current:
* --------------------
* Dereference all resources for the current power state. Power
* resources no longer referenced (new reference count of 0) are
* turned off.
*/
for (i = 0; i < current_list->count; i++) {
status = bm_get_device_context(current_list->handles[i],
(BM_DRIVER_CONTEXT*)(&pr));
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Invalid reference to power resource [%02x].\n", target_list->handles[i]));
continue;
}
if (--pr->reference_count == 0) {
/* TBD: Need ordering based upon resource_order */
status = bm_pr_set_state(pr, ACPI_STATE_D3);
if (ACPI_FAILURE(status)) {
/* TBD: How do we handle this? */
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to change power state for power resource [%02x].\n", current_list->handles[i]));
}
}
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_add_device (
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_POWER_RESOURCE *pr = NULL;
BM_DEVICE *device = NULL;
acpi_buffer buffer;
acpi_object acpi_object;
FUNCTION_TRACE("bm_pr_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding power resource [%02x].\n", device_handle));
if (!context || *context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
buffer.length = sizeof(acpi_object);
buffer.pointer = &acpi_object;
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new BM_POWER_RESOURCE structure.
*/
pr = acpi_os_callocate(sizeof(BM_POWER_RESOURCE));
if (!pr) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
pr->device_handle = device->handle;
pr->acpi_handle = device->acpi_handle;
/*
* Get information on this power resource.
*/
status = acpi_evaluate_object(pr->acpi_handle, NULL, NULL, &buffer);
if (ACPI_FAILURE(status)) {
goto end;
}
pr->system_level = acpi_object.power_resource.system_level;
pr->resource_order = acpi_object.power_resource.resource_order;
pr->state = ACPI_STATE_UNKNOWN;
pr->reference_count = 0;
/*
* Get the power resource's current state (ON|OFF).
*/
status = bm_pr_get_state(pr);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(pr);
}
else {
*context = pr;
bm_pr_print(pr);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_remove_device (
void **context)
{
acpi_status status = AE_OK;
BM_POWER_RESOURCE *pr = NULL;
FUNCTION_TRACE("bm_pr_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
pr = (BM_POWER_RESOURCE*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing power resource [%02x].\n", pr->device_handle));
acpi_os_free(pr);
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_pr_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bm_pr_initialize");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
criteria.type = BM_TYPE_POWER_RESOURCE;
driver.notify = &bm_pr_notify;
driver.request = &bm_pr_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_terminate
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bm_pr_terminate");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
criteria.type = BM_TYPE_POWER_RESOURCE;
driver.notify = &bm_pr_notify;
driver.request = &bm_pr_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_notify
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bm_pr_notify");
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = bm_pr_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = bm_pr_remove_device(context);
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_pr_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_pr_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
BM_POWER_RESOURCE *pr = NULL;
FUNCTION_TRACE("bm_pr_request");
/*
* Must have a valid request structure and context.
*/
if (!request || !context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* context contains information specific to this power resource.
*/
pr = (BM_POWER_RESOURCE*)context;
/*
* Handle request:
* ---------------
*/
switch (request->command) {
default:
status = AE_SUPPORT;
break;
}
request->status = status;
return_ACPI_STATUS(status);
}
/******************************************************************************
*
* Module Name: bmrequest.c
* $Revision: 16 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmrequest")
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_generate_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_generate_request (
BM_NODE *node,
BM_REQUEST *request)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
FUNCTION_TRACE("bm_generate_request");
if (!node || !request) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
device = &(node->device);
if (!BM_IS_DRIVER_CONTROL(device)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No driver installed for device [%02x].\n", device->handle));
return_ACPI_STATUS(AE_NOT_EXIST);
}
status = node->driver.request(request, node->driver.context);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_request (
BM_REQUEST *request)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
BM_DEVICE *device = NULL;
FUNCTION_TRACE("bm_request");
/*
* Must have a valid request structure.
*/
if (!request) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Received request for device [%02x] command [%02x].\n", request->handle, request->command));
/*
* Resolve the node.
*/
status = bm_get_node(request->handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
device = &(node->device);
/*
* Device-Specific Request?
* ------------------------
* If a device-specific command (>=0x80) forward this request to
* the appropriate driver.
*/
if (request->command & BM_COMMAND_DEVICE_SPECIFIC) {
status = bm_generate_request(node, request);
return_ACPI_STATUS(status);
}
/*
* Bus-Specific Requests:
* ----------------------
*/
switch (request->command) {
case BM_COMMAND_GET_POWER_STATE:
status = bm_get_power_state(node);
if (ACPI_FAILURE(status)) {
break;
}
status = bm_copy_to_buffer(&(request->buffer),
&(device->power.state), sizeof(BM_POWER_STATE));
break;
case BM_COMMAND_SET_POWER_STATE:
{
BM_POWER_STATE *power_state = NULL;
status = bm_cast_buffer(&(request->buffer),
(void**)&power_state, sizeof(BM_POWER_STATE));
if (ACPI_FAILURE(status)) {
break;
}
status = bm_set_power_state(node, *power_state);
}
break;
default:
status = AE_SUPPORT;
request->status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/******************************************************************************
*
* Module Name: bmsearch.c
* $Revision: 16 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmsearch")
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_compare
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_compare (
BM_DEVICE *device,
BM_DEVICE_ID *criteria)
{
if (!device || !criteria) {
return AE_BAD_PARAMETER;
}
/*
* Present?
* --------
* We're only going to match on devices that are present.
* TBD: Optimize in bm_search (don't have to call here).
*/
if (!BM_DEVICE_PRESENT(device)) {
return AE_NOT_FOUND;
}
/*
* Type?
*/
if (criteria->type && (criteria->type != device->id.type)) {
return AE_NOT_FOUND;
}
/*
* HID?
*/
if ((criteria->hid[0]) && (0 != STRNCMP(criteria->hid,
device->id.hid, sizeof(BM_DEVICE_HID)))) {
return AE_NOT_FOUND;
}
/*
* ADR?
*/
if ((criteria->adr) && (criteria->adr != device->id.adr)) {
return AE_NOT_FOUND;
}
return AE_OK;
}
/****************************************************************************
*
* FUNCTION: bm_search
*
* PARAMETERS:
*
* RETURN: AE_BAD_PARAMETER- invalid input parameter
* AE_NOT_EXIST - start_device_handle doesn't exist
* AE_NOT_FOUND - no matches to Search_info.criteria found
* AE_OK - success
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_search(
BM_HANDLE device_handle,
BM_DEVICE_ID *criteria,
BM_HANDLE_LIST *results)
{
acpi_status status = AE_OK;
BM_NODE *node = NULL;
FUNCTION_TRACE("bm_search");
if (!criteria || !results) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
results->count = 0;
/*
* Locate Starting Point:
* ----------------------
* Locate the node in the hierarchy where we'll begin our search.
*/
status = bm_get_node(device_handle, 0, &node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Parse Hierarchy:
* ----------------
* Parse through the node hierarchy looking for matches.
*/
while (node && (results->count<=BM_HANDLES_MAX)) {
/*
* Depth-first:
* ------------
* Searches are always performed depth-first.
*/
if (node->scope.head) {
status = bm_compare(&(node->device), criteria);
if (ACPI_SUCCESS(status)) {
results->handles[results->count++] =
node->device.handle;
}
node = node->scope.head;
}
/*
* Now Breadth:
* ------------
* Search all peers until scope is exhausted.
*/
else {
status = bm_compare(&(node->device), criteria);
if (ACPI_SUCCESS(status)) {
results->handles[results->count++] =
node->device.handle;
}
/*
* Locate Next Device:
* -------------------
* The next node is either a peer at this level
* (node->next is valid), or we work are way back
* up the tree until we either find a non-parsed
* peer or hit the top (node->parent is NULL).
*/
while (!node->next && node->parent) {
node = node->parent;
}
node = node->next;
}
}
if (results->count == 0) {
return_ACPI_STATUS(AE_NOT_FOUND);
}
else {
return_ACPI_STATUS(AE_OK);
}
}
/*****************************************************************************
*
* Module Name: bmutils.c
* $Revision: 43 $
*
*****************************************************************************/
/*
* 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 "bm.h"
#define _COMPONENT ACPI_BUS
MODULE_NAME ("bmutils")
#ifdef ACPI_DEBUG
#define DEBUG_EVAL_ERROR(l,h,p,s) bm_print_eval_error(l,h,p,s)
#else
#define DEBUG_EVAL_ERROR(l,h,p,s)
#endif
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: bm_print_eval_error
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
bm_print_eval_error (
u32 debug_level,
acpi_handle handle,
acpi_string pathname,
acpi_status status)
{
acpi_buffer buffer;
acpi_status local_status;
PROC_NAME("bm_print_eval_error");
buffer.length = 256;
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return;
}
local_status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
if (ACPI_FAILURE(local_status)) {
ACPI_DEBUG_PRINT((ACPI_DEBUG_LEVEL(debug_level), "Evaluate object [%p], %s\n", handle,
acpi_format_exception(status)));
return;
}
if (pathname) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate object [%s.%s], %s\n", (char*)buffer.pointer, pathname,
acpi_format_exception(status)));
}
else {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate object [%s], %s\n", (char*)buffer.pointer,
acpi_format_exception(status)));
}
acpi_os_free(buffer.pointer);
}
/****************************************************************************
*
* FUNCTION: bm_copy_to_buffer
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_copy_to_buffer (
acpi_buffer *buffer,
void *data,
u32 length)
{
FUNCTION_TRACE("bm_copy_to_buffer");
if (!buffer || (!buffer->pointer) || !data || (length == 0)) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (length > buffer->length) {
buffer->length = length;
return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
}
buffer->length = length;
MEMCPY(buffer->pointer, data, length);
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_cast_buffer
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_cast_buffer (
acpi_buffer *buffer,
void **pointer,
u32 length)
{
FUNCTION_TRACE("bm_cast_buffer");
if (!buffer || !buffer->pointer || !pointer || length == 0) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (length > buffer->length) {
return_ACPI_STATUS(AE_BAD_DATA);
}
*pointer = buffer->pointer;
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_extract_package_data
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_extract_package_data (
acpi_object *package,
acpi_buffer *format,
acpi_buffer *buffer)
{
u32 tail_offset = 0;
u32 size_required = 0;
char *format_string = NULL;
u32 format_count = 0;
u32 i = 0;
u8 *head = NULL;
u8 *tail = NULL;
FUNCTION_TRACE("bm_extract_package_data");
if (!package || (package->type != ACPI_TYPE_PACKAGE) || (package->package.count < 1)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'package' argument\n"));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (!format || !format->pointer || (format->length < 1)) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'format' argument\n"));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (!buffer) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'buffer' argument\n"));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
format_count = (format->length/sizeof(char)) - 1;
if (format_count > package->package.count) {
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Format specifies more objects [%d] than exist in package [%d].", format_count, package->package.count));
return_ACPI_STATUS(AE_BAD_DATA);
}
format_string = (char*)format->pointer;
/*
* Calculate size_required.
*/
for (i=0; i<format_count; i++) {
acpi_object *element = &(package->package.elements[i]);
if (!element) {
return_ACPI_STATUS(AE_BAD_DATA);
}
switch (element->type) {
case ACPI_TYPE_INTEGER:
switch (format_string[i]) {
case 'N':
size_required += sizeof(acpi_integer);
tail_offset += sizeof(acpi_integer);
break;
case 'S':
size_required += sizeof(char*) + sizeof(acpi_integer) + sizeof(char);
tail_offset += sizeof(char*);
break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d]: got number, expecing [%c].\n", i, format_string[i]));
return_ACPI_STATUS(AE_BAD_DATA);
break;
}
break;
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
switch (format_string[i]) {
case 'S':
size_required += sizeof(char*) + (element->string.length * sizeof(char)) + sizeof(char);
tail_offset += sizeof(char*);
break;
case 'B':
size_required += sizeof(u8*) + (element->buffer.length * sizeof(u8));
tail_offset += sizeof(u8*);
break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d] got string/buffer, expecing [%c].\n", i, format_string[i]));
return_ACPI_STATUS(AE_BAD_DATA);
break;
}
break;
case ACPI_TYPE_PACKAGE:
default:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unsupported element at index=%d\n", i));
/* TBD: handle nested packages... */
return_ACPI_STATUS(AE_SUPPORT);
break;
}
}
/*
* Validate output buffer.
*/
if (buffer->length < size_required) {
buffer->length = size_required;
return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
}
else if (buffer->length != size_required || !buffer->pointer) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
head = buffer->pointer;
tail = buffer->pointer + tail_offset;
/*
* Extract package data.
*/
for (i=0; i<format_count; i++) {
u8 **pointer = NULL;
acpi_object *element = &(package->package.elements[i]);
switch (element->type) {
case ACPI_TYPE_INTEGER:
switch (format_string[i]) {
case 'N':
*((acpi_integer*)head) = element->integer.value;
head += sizeof(acpi_integer);
break;
case 'S':
pointer = (u8**)head;
*pointer = tail;
*((acpi_integer*)tail) = element->integer.value;
head += sizeof(acpi_integer*);
tail += sizeof(acpi_integer);
/* NULL terminate string */
*tail = (char)0;
tail += sizeof(char);
break;
default:
/* Should never get here */
break;
}
break;
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
switch (format_string[i]) {
case 'S':
pointer = (u8**)head;
*pointer = tail;
memcpy(tail, element->string.pointer, element->string.length);
head += sizeof(char*);
tail += element->string.length * sizeof(char);
/* NULL terminate string */
*tail = (char)0;
tail += sizeof(char);
break;
case 'B':
pointer = (u8**)head;
*pointer = tail;
memcpy(tail, element->buffer.pointer, element->buffer.length);
head += sizeof(u8*);
tail += element->buffer.length * sizeof(u8);
break;
default:
/* Should never get here */
break;
}
break;
case ACPI_TYPE_PACKAGE:
/* TBD: handle nested packages... */
default:
/* Should never get here */
break;
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bm_evaluate_object
*
* PARAMETERS:
*
* RETURN: AE_OK
* AE_BUFFER_OVERFLOW Evaluated object returned data, but
* caller did not provide buffer.
*
* DESCRIPTION: Helper for acpi_evaluate_object that handles buffer
* allocation. Note that the caller is responsible for
* freeing buffer->pointer!
*
****************************************************************************/
acpi_status
bm_evaluate_object (
acpi_handle handle,
acpi_string pathname,
acpi_object_list *arguments,
acpi_buffer *buffer)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bm_evaluate_object");
/* If caller provided a buffer it must be unallocated/zero'd. */
if ((buffer) && (buffer->length != 0 || buffer->pointer)) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Evalute Object:
* ---------------
* The first attempt is just to get the size of the object data
* (that is unless there's no return data, e.g. _INI); the second
* gets the data.
*/
status = acpi_evaluate_object(handle, pathname, arguments, buffer);
if (ACPI_SUCCESS(status)) {
return_ACPI_STATUS(status);
}
else if ((buffer) && (status == AE_BUFFER_OVERFLOW)) {
/* Gotta allocate -- CALLER MUST FREE! */
buffer->pointer = acpi_os_callocate(buffer->length);
if (!buffer->pointer) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/* Re-evaluate -- this time it should work */
status = acpi_evaluate_object(handle, pathname,
arguments, buffer);
}
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
DEBUG_EVAL_ERROR(ACPI_LV_WARN, handle, pathname,
status);
}
if (buffer && buffer->pointer) {
acpi_os_free(buffer->pointer);
buffer->pointer = NULL;
buffer->length = 0;
}
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_evaluate_simple_integer
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_evaluate_simple_integer (
acpi_handle handle,
acpi_string pathname,
u32 *data)
{
acpi_status status = AE_OK;
acpi_object *element = NULL;
acpi_buffer buffer;
FUNCTION_TRACE("bm_evaluate_simple_integer");
if (!data) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&buffer, 0, sizeof(acpi_buffer));
/*
* Evaluate Object:
* ----------------
*/
status = bm_evaluate_object(handle, pathname, NULL, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "failed to evaluate object (%s)\n",
acpi_format_exception(status)));
goto end;
}
/*
* Validate Data:
* --------------
*/
status = bm_cast_buffer(&buffer, (void**)&element,
sizeof(acpi_object));
if (ACPI_FAILURE(status)) {
DEBUG_EVAL_ERROR(ACPI_LV_WARN, handle, pathname, status);
goto end;
}
if (element->type != ACPI_TYPE_INTEGER) {
status = AE_BAD_DATA;
DEBUG_EVAL_ERROR(ACPI_LV_WARN, handle, pathname, status);
goto end;
}
*data = element->integer.value;
end:
acpi_os_free(buffer.pointer);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bm_evaluate_reference_list
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bm_evaluate_reference_list (
acpi_handle handle,
acpi_string pathname,
BM_HANDLE_LIST *reference_list)
{
acpi_status status = AE_OK;
acpi_object *package = NULL;
acpi_object *element = NULL;
acpi_handle reference_handle = NULL;
acpi_buffer buffer;
u32 i = 0;
FUNCTION_TRACE("bm_evaluate_reference_list");
if (!reference_list) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&buffer, 0, sizeof(acpi_buffer));
/*
* Evaluate Object:
* ----------------
*/
status = bm_evaluate_object(handle, pathname, NULL, &buffer);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Validate Package:
* -----------------
*/
status = bm_cast_buffer(&buffer, (void**)&package,
sizeof(acpi_object));
if (ACPI_FAILURE(status)) {
DEBUG_EVAL_ERROR(ACPI_LV_WARN, handle, pathname, status);
goto end;
}
if (package->type != ACPI_TYPE_PACKAGE) {
status = AE_BAD_DATA;
DEBUG_EVAL_ERROR(ACPI_LV_WARN, handle, pathname, status);
goto end;
}
if (package->package.count > BM_HANDLES_MAX) {
package->package.count = BM_HANDLES_MAX;
}
/*
* Parse Package Data:
* -------------------
*/
for (i = 0; i < package->package.count; i++) {
element = &(package->package.elements[i]);
if (!element || (element->type != ACPI_TYPE_STRING)) {
status = AE_BAD_DATA;
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid element in package (not a device reference).\n"));
DEBUG_EVAL_ERROR (ACPI_LV_WARN, handle, pathname, status);
break;
}
/*
* Resolve reference string (e.g. "\_PR_.CPU_") to an
* acpi_handle.
*/
status = acpi_get_handle(handle,
element->string.pointer, &reference_handle);
if (ACPI_FAILURE(status)) {
status = AE_BAD_DATA;
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to resolve device reference [%s].\n", element->string.pointer));
DEBUG_EVAL_ERROR (ACPI_LV_WARN, handle, pathname, status);
break;
}
/*
* Resolve acpi_handle to BM_HANDLE.
*/
status = bm_get_handle(reference_handle,
&(reference_list->handles[i]));
if (ACPI_FAILURE(status)) {
status = AE_BAD_DATA;
ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to resolve device reference for [%p].\n", reference_handle));
DEBUG_EVAL_ERROR (ACPI_LV_WARN, handle, pathname, status);
break;
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resolved reference [%s]->[%p]->[%02x]\n", element->string.pointer, reference_handle, reference_list->handles[i]));
(reference_list->count)++;
}
end:
acpi_os_free(buffer.pointer);
return_ACPI_STATUS(status);
}
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
/*****************************************************************************
*
* Module Name: bn.c
* $Revision: 27 $
*
*****************************************************************************/
/*
* 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 Plxxe, Suite 330, Boston, MA 02111-1307 USA
*/
#include <acpi.h>
#include "bn.h"
#define _COMPONENT ACPI_BUTTON
MODULE_NAME ("bn")
/*****************************************************************************
* Internal Functions
*****************************************************************************/
/*****************************************************************************
*
* FUNCTION: bn_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific button.
*
****************************************************************************/
void
bn_print (
BN_CONTEXT *button)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
PROC_NAME("bn_print");
if (!button) {
return;
}
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(button->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic button information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
switch (button->type) {
case BN_TYPE_POWER_BUTTON:
case BN_TYPE_POWER_BUTTON_FIXED:
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Power_button[%02x]:[%p] %s\n", button->device_handle, button->acpi_handle, (char*)buffer.pointer));
break;
case BN_TYPE_SLEEP_BUTTON:
case BN_TYPE_SLEEP_BUTTON_FIXED:
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Sleep_button[%02x]:[%p] %s\n", button->device_handle, button->acpi_handle, (char*)buffer.pointer));
break;
case BN_TYPE_LID_SWITCH:
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Lid_switch[%02x]:[%p] %s\n", button->device_handle, button->acpi_handle, (char*)buffer.pointer));
break;
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: bn_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_add_device(
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
BN_CONTEXT *button = NULL;
FUNCTION_TRACE("bn_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding button device [%02x].\n", device_handle));
if (!context || *context) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid context.\n"));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Get information on this device.
*/
status = bm_get_device_info( device_handle, &device );
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new BN_CONTEXT structure.
*/
button = acpi_os_callocate(sizeof(BN_CONTEXT));
if (!button) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
button->device_handle = device->handle;
button->acpi_handle = device->acpi_handle;
/*
* Power Button?
* -------------
* Either fixed-feature or generic (namespace) types.
*/
if (strncmp(device->id.hid, BN_HID_POWER_BUTTON,
sizeof(BM_DEVICE_HID)) == 0) {
if (device->id.type == BM_TYPE_FIXED_BUTTON) {
button->type = BN_TYPE_POWER_BUTTON_FIXED;
/* Register for fixed-feature events. */
status = acpi_install_fixed_event_handler(
ACPI_EVENT_POWER_BUTTON, bn_notify_fixed,
(void*)button);
}
else {
button->type = BN_TYPE_POWER_BUTTON;
}
}
/*
* Sleep Button?
* -------------
* Either fixed-feature or generic (namespace) types.
*/
else if (strncmp( device->id.hid, BN_HID_SLEEP_BUTTON,
sizeof(BM_DEVICE_HID)) == 0) {
if (device->id.type == BM_TYPE_FIXED_BUTTON) {
button->type = BN_TYPE_SLEEP_BUTTON_FIXED;
/* Register for fixed-feature events. */
status = acpi_install_fixed_event_handler(
ACPI_EVENT_SLEEP_BUTTON, bn_notify_fixed,
(void*)button);
}
else {
button->type = BN_TYPE_SLEEP_BUTTON;
}
}
/*
* LID Switch?
* -----------
*/
else if (strncmp( device->id.hid, BN_HID_LID_SWITCH,
sizeof(BM_DEVICE_HID)) == 0) {
button->type = BN_TYPE_LID_SWITCH;
}
status = bn_osl_add_device(button);
if (ACPI_FAILURE(status)) {
goto end;
}
*context = button;
bn_print(button);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(button);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bn_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_remove_device(
void **context)
{
acpi_status status = AE_OK;
BN_CONTEXT *button = NULL;
FUNCTION_TRACE("bn_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
button = (BN_CONTEXT*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing button device [%02x].\n", button->device_handle));
/*
* Unregister for fixed-feature events.
*/
switch (button->type) {
case BN_TYPE_POWER_BUTTON_FIXED:
status = acpi_remove_fixed_event_handler(
ACPI_EVENT_POWER_BUTTON, bn_notify_fixed);
break;
case BN_TYPE_SLEEP_BUTTON_FIXED:
status = acpi_remove_fixed_event_handler(
ACPI_EVENT_SLEEP_BUTTON, bn_notify_fixed);
break;
}
bn_osl_remove_device(button);
acpi_os_free(button);
*context = NULL;
return_ACPI_STATUS(status);
}
/*****************************************************************************
* External Functions
*****************************************************************************/
/*****************************************************************************
*
* FUNCTION: bn_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_initialize (void)
{
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bn_initialize");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
driver.notify = &bn_notify;
driver.request = &bn_request;
/*
* Register for power buttons.
*/
MEMCPY(criteria.hid, BN_HID_POWER_BUTTON, sizeof(BN_HID_POWER_BUTTON));
bm_register_driver(&criteria, &driver);
/*
* Register for sleep buttons.
*/
MEMCPY(criteria.hid, BN_HID_SLEEP_BUTTON, sizeof(BN_HID_SLEEP_BUTTON));
bm_register_driver(&criteria, &driver);
/*
* Register for LID switches.
*/
MEMCPY(criteria.hid, BN_HID_LID_SWITCH, sizeof(BN_HID_LID_SWITCH));
bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bn_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("bn_terminate");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
driver.notify = &bn_notify;
driver.request = &bn_request;
/*
* Unregister for power buttons.
*/
MEMCPY(criteria.hid, BN_HID_POWER_BUTTON, sizeof(BN_HID_POWER_BUTTON));
status = bm_unregister_driver(&criteria, &driver);
/*
* Unregister for sleep buttons.
*/
MEMCPY(criteria.hid, BN_HID_SLEEP_BUTTON, sizeof(BN_HID_SLEEP_BUTTON));
status = bm_unregister_driver(&criteria, &driver);
/*
* Unregister for LID switches.
*/
MEMCPY(criteria.hid, BN_HID_LID_SWITCH, sizeof(BN_HID_LID_SWITCH));
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bn_notify_fixed
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_notify_fixed (
void *context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bn_notify_fixed");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Status change event detected.\n"));
status = bn_osl_generate_event(BN_NOTIFY_STATUS_CHANGE,
((BN_CONTEXT*)context));
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bn_notify
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bn_notify");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = bn_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = bn_remove_device(context);
break;
case BN_NOTIFY_STATUS_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Status change event detected.\n"));
status = bn_osl_generate_event(BN_NOTIFY_STATUS_CHANGE,
((BN_CONTEXT*)*context));
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: bn_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
bn_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("bn_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);
}
/******************************************************************************
*
* Module Name: bn_osl.c
* $Revision: 16 $
*
*****************************************************************************/
/*
* 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 <acpi.h>
#include "bn.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - Button Driver");
MODULE_LICENSE("GPL");
#define BN_PROC_ROOT "button"
#define BN_PROC_POWER_BUTTON "power"
#define BN_PROC_SLEEP_BUTTON "sleep"
#define BN_PROC_LID_SWITCH "lid"
extern struct proc_dir_entry *bm_proc_root;
static struct proc_dir_entry *bn_proc_root = NULL;
#define BN_TYPE_UNKNOWN 0
#define BN_TYPE_FIXED 1
#define BN_TYPE_GENERIC 2
static int bn_power_button = BN_TYPE_UNKNOWN;
static int bn_sleep_button = BN_TYPE_UNKNOWN;
static int bn_lid_switch = BN_TYPE_UNKNOWN;
/****************************************************************************
*
* FUNCTION: bn_osl_add_device
*
****************************************************************************/
acpi_status
bn_osl_add_device(
BN_CONTEXT *button)
{
acpi_status status = AE_OK;
if (!button) {
return(AE_BAD_PARAMETER);
}
switch (button->type) {
case BN_TYPE_POWER_BUTTON_FIXED:
bn_power_button = BN_TYPE_FIXED;
printk(KERN_INFO "ACPI: Power Button (FF) found\n");
if (!proc_mkdir(BN_PROC_POWER_BUTTON, bn_proc_root)) {
status = AE_ERROR;
}
break;
case BN_TYPE_POWER_BUTTON:
/*
* Avoid creating multiple /proc entries when (buggy) ACPI
* BIOS tables erroneously list both fixed- and generic-
* feature buttons. Note that fixed-feature buttons are
* always enumerated first (and there can only be one) so
* we only need to check here.
*/
switch (bn_power_button) {
case BN_TYPE_GENERIC:
printk(KERN_WARNING "ACPI: Multiple generic-space power buttons detected, using first\n");
break;
case BN_TYPE_FIXED:
printk(KERN_WARNING "ACPI: Multiple power buttons detected, ignoring fixed-feature\n");
default:
printk(KERN_INFO "ACPI: Power Button (CM) found\n");
bn_power_button = BN_TYPE_GENERIC;
if (!proc_mkdir(BN_PROC_POWER_BUTTON, bn_proc_root)) {
status = AE_ERROR;
}
break;
}
break;
case BN_TYPE_SLEEP_BUTTON_FIXED:
bn_sleep_button = BN_TYPE_FIXED;
printk(KERN_INFO "ACPI: Sleep Button (FF) found\n");
if (!proc_mkdir(BN_PROC_SLEEP_BUTTON, bn_proc_root)) {
status = AE_ERROR;
}
break;
case BN_TYPE_SLEEP_BUTTON:
/*
* Avoid creating multiple /proc entries when (buggy) ACPI
* BIOS tables erroneously list both fixed- and generic-
* feature buttons. Note that fixed-feature buttons are
* always enumerated first (and there can only be one) so
* we only need to check here.
*/
switch (bn_sleep_button) {
case BN_TYPE_GENERIC:
printk(KERN_WARNING "ACPI: Multiple generic-space sleep buttons detected, using first\n");
break;
case BN_TYPE_FIXED:
printk(KERN_WARNING "ACPI: Multiple sleep buttons detected, ignoring fixed-feature\n");
default:
bn_sleep_button = BN_TYPE_GENERIC;
printk(KERN_INFO "ACPI: Sleep Button (CM) found\n");
if (!proc_mkdir(BN_PROC_SLEEP_BUTTON, bn_proc_root)) {
status = AE_ERROR;
}
break;
}
break;
case BN_TYPE_LID_SWITCH:
if (bn_lid_switch) {
printk(KERN_WARNING "ACPI: Multiple generic-space lid switches detected, using first\n");
break;
}
bn_lid_switch = BN_TYPE_GENERIC;
printk(KERN_INFO "ACPI: Lid Switch (CM) found\n");
if (!proc_mkdir(BN_PROC_LID_SWITCH, bn_proc_root)) {
status = AE_ERROR;
}
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: bn_osl_remove_device
*
****************************************************************************/
acpi_status
bn_osl_remove_device (
BN_CONTEXT *button)
{
if (!button) {
return(AE_BAD_PARAMETER);
}
switch (button->type) {
case BN_TYPE_POWER_BUTTON:
case BN_TYPE_POWER_BUTTON_FIXED:
remove_proc_entry(BN_PROC_POWER_BUTTON, bn_proc_root);
break;
case BN_TYPE_SLEEP_BUTTON:
case BN_TYPE_SLEEP_BUTTON_FIXED:
remove_proc_entry(BN_PROC_SLEEP_BUTTON, bn_proc_root);
break;
case BN_TYPE_LID_SWITCH:
remove_proc_entry(BN_PROC_LID_SWITCH, bn_proc_root);
break;
}
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: bn_osl_generate_event
*
****************************************************************************/
acpi_status
bn_osl_generate_event (
u32 event,
BN_CONTEXT *button)
{
acpi_status status = AE_OK;
if (!button) {
return(AE_BAD_PARAMETER);
}
switch (event) {
case BN_NOTIFY_STATUS_CHANGE:
switch(button->type) {
case BN_TYPE_POWER_BUTTON:
case BN_TYPE_POWER_BUTTON_FIXED:
status = bm_osl_generate_event(button->device_handle,
BN_PROC_ROOT, BN_PROC_POWER_BUTTON, event, 0);
break;
case BN_TYPE_SLEEP_BUTTON:
case BN_TYPE_SLEEP_BUTTON_FIXED:
status = bm_osl_generate_event(button->device_handle,
BN_PROC_ROOT, BN_PROC_SLEEP_BUTTON, event, 0);
break;
case BN_TYPE_LID_SWITCH:
status = bm_osl_generate_event(button->device_handle,
BN_PROC_ROOT, BN_PROC_LID_SWITCH, event, 0);
break;
default:
status = AE_SUPPORT;
break;
}
break;
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: bn_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
bn_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
bn_proc_root = proc_mkdir(BN_PROC_ROOT, bm_proc_root);
if (!bn_proc_root) {
status = AE_ERROR;
}
else {
status = bn_initialize();
if (ACPI_FAILURE(status)) {
remove_proc_entry(BN_PROC_ROOT, bm_proc_root);
}
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: bn_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
bn_osl_cleanup (void)
{
bn_terminate();
if (bn_proc_root) {
remove_proc_entry(BN_PROC_ROOT, bm_proc_root);
}
return;
}
module_init(bn_osl_init);
module_exit(bn_osl_cleanup);
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
/*****************************************************************************
*
* Module Name: ec_osl.c
* $Revision: 11 $
*
*****************************************************************************/
/*
* 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 <acpi.h>
#include <bm.h>
#include "ec.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - Embedded Controller Driver");
MODULE_LICENSE("GPL");
extern struct proc_dir_entry *bm_proc_root;
/****************************************************************************
*
* FUNCTION: ec_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
ec_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
status = ec_initialize();
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: ec_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
ec_osl_cleanup(void)
{
ec_terminate();
return;
}
module_init(ec_osl_init);
module_exit(ec_osl_cleanup);
/*****************************************************************************
*
* Module Name: ecgpe.c
* $Revision: 28 $
*
*****************************************************************************/
/*
* 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 "ec.h"
#define _COMPONENT ACPI_EC
MODULE_NAME ("ecgpe")
/****************************************************************************
*
* FUNCTION: ec_query_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
ec_query_handler (
void *context)
{
EC_CONTEXT *ec = (EC_CONTEXT*)context;
static char object_name[5] = {'_','Q','0','0','\0'};
const char hex[] = {'0','1','2','3','4','5','6','7','8',
'9','A','B','C','D','E','F'};
FUNCTION_TRACE("ec_query_handler");
if (!ec) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return_VOID;
}
/*
* Evaluate _Qxx:
* --------------
* Evaluate corresponding _Qxx method. Note that a zero query value
* indicates a spurious EC_SCI (no such thing as _Q00).
*/
object_name[2] = hex[((ec->query_data >> 4) & 0x0F)];
object_name[3] = hex[(ec->query_data & 0x0F)];
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Evaluating [%s] for ec [%02x].\n", object_name, ec->device_handle));
bm_evaluate_object(ec->acpi_handle, object_name, NULL, NULL);
return_VOID;
}
/****************************************************************************
*
* FUNCTION: ec_gpe_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
ec_gpe_handler (
void *context)
{
acpi_status status = AE_OK;
EC_CONTEXT *ec = (EC_CONTEXT*)context;
EC_STATUS ec_status = 0;
FUNCTION_TRACE("ec_gpe_handler");
if (!ec) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return_VOID;
}
/* TBD: synchronize w/ transaction (ectransx). */
/*
* EC_SCI?
* -------
* Check the EC_SCI bit to see if this is an EC_SCI event. If not (e.g.
* OBF/IBE) just return, as we already poll to detect these events.
*/
acpi_os_read_port(ec->status_port, &ec_status, 8);
if (!(ec_status & EC_FLAG_SCI)) {
return_VOID;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "EC_SCI event detected on ec [%02x] - running query.\n", ec->device_handle));
/*
* Run Query:
* ----------
* Query the EC to find out which _Qxx method we need to evaluate.
* Note that successful completion of the query causes the EC_SCI
* bit to be cleared (and thus clearing the interrupt source).
*/
status = ec_io_write(ec, ec->command_port, EC_COMMAND_QUERY,
EC_EVENT_OUTPUT_BUFFER_FULL);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'query command' to EC.\n"));
return_VOID;
}
status = ec_io_read(ec, ec->data_port, &(ec->query_data),
EC_EVENT_NONE);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Error reading query data.\n"));
return_VOID;
}
/* TBD: un-synchronize w/ transaction (ectransx). */
/*
* Spurious EC_SCI?
* ----------------
*/
if (!ec->query_data) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Spurious EC SCI detected.\n"));
return_VOID;
}
/*
* Defer _Qxx Execution:
* ---------------------
* Can't evaluate this method now 'cause we're at interrupt-level.
*/
status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
ec_query_handler, ec);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to defer _Qxx method evaluation.\n"));
return_VOID;
}
return_VOID;
}
/****************************************************************************
*
* FUNCTION: ec_install_gpe_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_install_gpe_handler (
EC_CONTEXT *ec)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_install_gpe_handler");
if (!ec) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Evaluate _GPE:
* --------------
* Evaluate the "_GPE" object (required) to find out which GPE bit
* is used by this EC to signal events (SCIs).
*/
status = bm_evaluate_simple_integer(ec->acpi_handle,
"_GPE", &(ec->gpe_bit));
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Install GPE Handler:
* --------------------
* Install a handler for this EC's GPE bit.
*/
status = acpi_install_gpe_handler(ec->gpe_bit, ACPI_EVENT_EDGE_TRIGGERED,
&ec_gpe_handler, ec);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "acpi_install_gpe_handler() failed for GPE bit [%02x] with status [%08x].\n", ec->gpe_bit, status));
ec->gpe_bit = EC_GPE_UNKNOWN;
return_ACPI_STATUS(status);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_remove_gpe_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_remove_gpe_handler (
EC_CONTEXT *ec)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_remove_gpe_handler");
if (!ec) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
status = acpi_remove_gpe_handler(ec->gpe_bit, &ec_gpe_handler);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* Module Name: ecmain.c
* $Revision: 29 $
*
*****************************************************************************/
/*
* 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 "ec.h"
#define _COMPONENT ACPI_EC
MODULE_NAME ("ecmain")
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: ec_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific ec.
*
****************************************************************************/
void
ec_print (
EC_CONTEXT *ec)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
#endif /*ACPI_DEBUG*/
PROC_NAME("ec_print");
if (!ec) {
return;
}
acpi_os_printf("EC: found, GPE %d\n", ec->gpe_bit);
#ifdef ACPI_DEBUG
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(ec->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic thermal zone information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Embedded_controller[%02x]:[%p] %s\n", ec->device_handle, ec->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| gpe_bit[%02x] status/command_port[%02x] data_port[%02x]\n", ec->gpe_bit, ec->status_port, ec->data_port));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: ec_get_port_values
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Evaluate _CRS to get the current resources (I/O port
* addresses) for this EC.
*
****************************************************************************/
acpi_status
ec_get_port_values(
EC_CONTEXT *ec)
{
acpi_status status = AE_OK;
acpi_buffer buffer;
acpi_resource *resource = NULL;
FUNCTION_TRACE("ec_get_port_values");
if (!ec) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
buffer.length = 0;
buffer.pointer = NULL;
status = acpi_get_current_resources(ec->acpi_handle, &buffer);
if (status != AE_BUFFER_OVERFLOW) {
return_ACPI_STATUS(status);
}
buffer.pointer = acpi_os_callocate(buffer.length);
if (!buffer.pointer) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
status = acpi_get_current_resources(ec->acpi_handle, &buffer);
if (ACPI_FAILURE(status)) {
goto end;
}
resource = (acpi_resource *) buffer.pointer;
ec->data_port = resource->data.io.min_base_address;
resource = NEXT_RESOURCE(resource);
ec->status_port = ec->command_port =
resource->data.io.min_base_address;
end:
acpi_os_free(buffer.pointer);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_add_device(
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
BM_DEVICE *device = NULL;
EC_CONTEXT *ec = NULL;
u8 gpe_handler = FALSE;
u8 space_handler = FALSE;
FUNCTION_TRACE("ec_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding EC device [%02x].\n", device_handle));
if (!context || *context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new EC_CONTEXT structure.
*/
ec = acpi_os_callocate(sizeof(EC_CONTEXT));
if (!ec) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
ec->device_handle = device->handle;
ec->acpi_handle = device->acpi_handle;
/*
* Get the I/O port addresses for the command/status and data ports.
*/
status = ec_get_port_values(ec);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* See if we need to obtain the global lock for EC transactions.
*/
status = bm_evaluate_simple_integer(ec->acpi_handle, "_GLK",
&ec->use_global_lock);
if (status == AE_NOT_FOUND) {
ec->use_global_lock = 0;
}
else if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "EC _GLK failed\n"));
goto end;
}
/*
* Install a handler for servicing this EC's GPE.
*/
status = ec_install_gpe_handler(ec);
if (ACPI_FAILURE(status)) {
goto end;
}
else {
gpe_handler = TRUE;
}
/*
* Install a handler for servicing this EC's address space.
*/
status = ec_install_space_handler(ec);
if (ACPI_FAILURE(status)) {
goto end;
}
else {
space_handler = TRUE;
}
/*
* Create a semaphore to serialize EC transactions.
*/
status = acpi_os_create_semaphore(1,1, &(ec->mutex));
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Context now contains information specific to this EC. Note
* that we'll get this pointer back on every ec_request() and
* ec_notify().
*/
*context = ec;
ec_print(ec);
end:
if (ACPI_FAILURE(status)) {
if (gpe_handler) {
ec_remove_gpe_handler(ec);
}
if (space_handler) {
ec_remove_space_handler(ec);
}
if (ec->mutex) {
acpi_os_delete_semaphore(ec->mutex);
}
acpi_os_free(ec);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_remove_device(
void **context)
{
acpi_status status = AE_OK;
EC_CONTEXT *ec = NULL;
FUNCTION_TRACE("ec_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ec = (EC_CONTEXT*)*context;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing EC device [%02x].\n", ec->device_handle));
ec_remove_space_handler(ec);
ec_remove_gpe_handler(ec);
if (ec->mutex) {
acpi_os_delete_semaphore(ec->mutex);
}
acpi_os_free(ec);
*context = NULL;
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: ec_initialize
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("ec_initialize");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
/*
* Register driver for AC Adapter devices.
*/
MEMCPY(criteria.hid, EC_HID_EC, sizeof(EC_HID_EC));
driver.notify = &ec_notify;
driver.request = &ec_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_terminate
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_terminate(void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("ec_terminate");
MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
MEMSET(&driver, 0, sizeof(BM_DRIVER));
/*
* Unregister driver for AC Adapter devices.
*/
MEMCPY(criteria.hid, EC_HID_EC, sizeof(EC_HID_EC));
driver.notify = &ec_notify;
driver.request = &ec_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_notify
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_notify (
BM_NOTIFY notify,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_notify");
switch (notify) {
case BM_NOTIFY_DEVICE_ADDED:
status = ec_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = ec_remove_device(context);
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
EC_REQUEST *ec_request = NULL;
EC_CONTEXT *ec = NULL;
FUNCTION_TRACE("ec_request");
/*
* Must have a valid request structure and context.
*/
if (!request || !context)
return_ACPI_STATUS(AE_BAD_PARAMETER);
/*
* buffer must contain a valid EC_REQUEST structure.
*/
status = bm_cast_buffer(&(request->buffer), (void**)&ec_request,
sizeof(EC_REQUEST));
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
/*
* context contains information specific to this EC.
*/
ec = (EC_CONTEXT*)context;
/*
* Perform the Transaction.
*/
status = ec_transaction(ec, ec_request);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* Module Name: ecspace.c
* $Revision: 23 $
*
*****************************************************************************/
/*
* 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 "ec.h"
#define _COMPONENT ACPI_EC
MODULE_NAME ("ecspace")
/****************************************************************************
*
* FUNCTION: ec_space_setup
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_space_setup (
acpi_handle region_handle,
u32 function,
void *handler_context,
void **return_context)
{
/*
* The EC object is in the handler context and is needed
* when calling the ec_space_handler.
*/
*return_context = handler_context;
return AE_OK;
}
/****************************************************************************
*
* FUNCTION: ec_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (should be 8)
* value - Pointer to in or out value
* context - context pointer
*
* RETURN:
*
* DESCRIPTION: Handler for the Embedded Controller (EC) address space
* (Op Region)
*
****************************************************************************/
acpi_status
ec_space_handler (
u32 function,
ACPI_PHYSICAL_ADDRESS address,
u32 bit_width,
u32 *value,
void *handler_context,
void *region_context)
{
acpi_status status = AE_OK;
EC_CONTEXT *ec = NULL;
EC_REQUEST ec_request;
FUNCTION_TRACE("ec_space_handler");
if (address > 0xFF || bit_width != 8 || !value || !handler_context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ec = (EC_CONTEXT*)handler_context;
switch (function) {
case ACPI_READ_ADR_SPACE:
ec_request.command = EC_COMMAND_READ;
ec_request.address = address;
ec_request.data = 0;
break;
case ACPI_WRITE_ADR_SPACE:
ec_request.command = EC_COMMAND_WRITE;
ec_request.address = address;
ec_request.data = (u8)(*value);
break;
default:
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Received request with invalid function [%X].\n", function));
return_ACPI_STATUS(AE_BAD_PARAMETER);
break;
}
/*
* Perform the Transaction.
*/
status = ec_transaction(ec, &ec_request);
if (ACPI_SUCCESS(status)) {
(*value) = (u32)ec_request.data;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_install_space_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_install_space_handler (
EC_CONTEXT *ec)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_install_space_handler");
if (!ec) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
status = acpi_install_address_space_handler (ec->acpi_handle,
ACPI_ADR_SPACE_EC, &ec_space_handler, &ec_space_setup, ec);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_remove_space_handler
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_remove_space_handler (
EC_CONTEXT *ec)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_remove_space_handler");
if (!ec) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
status = acpi_remove_address_space_handler(ec->acpi_handle,
ACPI_ADR_SPACE_EC, &ec_space_handler);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* Module Name: ectransx.c
* $Revision: 24 $
*
*****************************************************************************/
/*
* 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 "ec.h"
#define _COMPONENT ACPI_EC
MODULE_NAME ("ectransx")
/****************************************************************************
*
* FUNCTION: ec_io_wait
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_io_wait (
EC_CONTEXT *ec,
EC_EVENT wait_event)
{
EC_STATUS ec_status = 0;
u32 i = 100;
if (!ec || ((wait_event != EC_EVENT_OUTPUT_BUFFER_FULL)
&& (wait_event != EC_EVENT_INPUT_BUFFER_EMPTY))) {
return(AE_BAD_PARAMETER);
}
/*
* Wait for Event:
* ---------------
* Poll the EC status register waiting for the event to occur.
* Note that we'll wait a maximum of 1ms in 10us chunks.
*/
switch (wait_event) {
case EC_EVENT_OUTPUT_BUFFER_FULL:
do {
acpi_os_read_port(ec->status_port, &ec_status, 8);
if (ec_status & EC_FLAG_OUTPUT_BUFFER) {
return(AE_OK);
}
acpi_os_stall(10);
} while (--i>0);
break;
case EC_EVENT_INPUT_BUFFER_EMPTY:
do {
acpi_os_read_port(ec->status_port, &ec_status, 8);
if (!(ec_status & EC_FLAG_INPUT_BUFFER)) {
return(AE_OK);
}
acpi_os_stall(10);
} while (--i>0);
break;
}
return(AE_TIME);
}
/****************************************************************************
*
* FUNCTION: ec_io_read
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_io_read (
EC_CONTEXT *ec,
ACPI_IO_ADDRESS io_port,
u8 *data,
EC_EVENT wait_event)
{
acpi_status status = AE_OK;
if (!ec || !data) {
return(AE_BAD_PARAMETER);
}
acpi_os_read_port(io_port, (u32*) data, 8);
if (wait_event) {
status = ec_io_wait(ec, wait_event);
}
return(status);
}
/****************************************************************************
*
* FUNCTION: ec_io_write
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_io_write (
EC_CONTEXT *ec,
ACPI_IO_ADDRESS io_port,
u8 data,
EC_EVENT wait_event)
{
acpi_status status = AE_OK;
if (!ec) {
return(AE_BAD_PARAMETER);
}
acpi_os_write_port(io_port, data, 8);
if (wait_event) {
status = ec_io_wait(ec, wait_event);
}
return(status);
}
/****************************************************************************
*
* FUNCTION: ec_read
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_read (
EC_CONTEXT *ec,
u8 address,
u8 *data)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_read");
if (!ec || !data) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (ec->use_global_lock) {
status = acpi_acquire_global_lock();
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Could not acquire Global Lock\n"));
return_ACPI_STATUS(status);
}
}
status = ec_io_write(ec, ec->command_port, EC_COMMAND_READ,
EC_EVENT_INPUT_BUFFER_EMPTY);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'read command' to EC.\n"));
return_ACPI_STATUS(status);
}
status = ec_io_write(ec, ec->data_port, address,
EC_EVENT_OUTPUT_BUFFER_FULL);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'read address' to EC.\n"));
return_ACPI_STATUS(status);
}
status = ec_io_read(ec, ec->data_port, data, EC_EVENT_NONE);
if (ec->use_global_lock) {
acpi_release_global_lock();
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Read data [%02x] from address [%02x] on ec [%02x].\n", (*data), address, ec->device_handle));
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_write
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_write (
EC_CONTEXT *ec,
u8 address,
u8 data)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_write");
if (!ec)
return_ACPI_STATUS(AE_BAD_PARAMETER);
if (ec->use_global_lock) {
status = acpi_acquire_global_lock();
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Could not acquire Global Lock\n"));
return_ACPI_STATUS(status);
}
}
status = ec_io_write(ec, ec->command_port, EC_COMMAND_WRITE,
EC_EVENT_INPUT_BUFFER_EMPTY);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'write command' to EC.\n"));
return_ACPI_STATUS(status);
}
status = ec_io_write(ec, ec->data_port, address,
EC_EVENT_INPUT_BUFFER_EMPTY);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'write address' to EC.\n"));
return_ACPI_STATUS(status);
}
status = ec_io_write(ec, ec->data_port, data,
EC_EVENT_INPUT_BUFFER_EMPTY);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to send 'write data' to EC.\n"));
return_ACPI_STATUS(status);
}
if (ec->use_global_lock) {
acpi_release_global_lock();
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Wrote data [%02x] to address [%02x] on ec [%02x].\n", data, address, ec->device_handle));
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: ec_transaction
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
ec_transaction (
EC_CONTEXT *ec,
EC_REQUEST *request)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("ec_transaction");
if (!ec || !request) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Obtain mutex to serialize all EC transactions.
*/
status = acpi_os_wait_semaphore(ec->mutex, 1, EC_DEFAULT_TIMEOUT);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Perform the transaction.
*/
switch (request->command) {
case EC_COMMAND_READ:
status = ec_read(ec, request->address, &(request->data));
break;
case EC_COMMAND_WRITE:
status = ec_write(ec, request->address, request->data);
break;
default:
status = AE_SUPPORT;
break;
}
/*
* Signal the mutex to indicate transaction completion.
*/
acpi_os_signal_semaphore(ec->mutex, 1);
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* Module Name: ac.h
* $Revision: 6 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __AC_H__
#define __AC_H__
#include <actypes.h>
#include <acexcep.h>
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
/*
* Notifications:
* --------------
*/
#define AC_NOTIFY_STATUS_CHANGE ((BM_NOTIFY) 0x80)
/*
* Hardware IDs:
* -------------
*/
#define AC_HID_AC_ADAPTER "ACPI0003"
/*
* Device Context:
* ---------------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
char uid[9];
u32 is_online;
} AC_CONTEXT;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
acpi_status
ac_initialize (void);
acpi_status
ac_terminate (void);
acpi_status
ac_notify (
u32 notify_type,
u32 device,
void **context);
acpi_status
ac_request(
BM_REQUEST *request_info,
void *context);
/* AC Adapter Driver OSL */
acpi_status
ac_osl_add_device (
AC_CONTEXT *ac_adapter);
acpi_status
ac_osl_remove_device (
AC_CONTEXT *ac_adapter);
acpi_status
ac_osl_generate_event (
u32 event,
AC_CONTEXT *ac_adapter);
#endif /* __AC_H__ */
/*****************************************************************************
*
* Module name: bm.h
* $Revision: 41 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __BM_H__
#define __BM_H__
#include <actypes.h>
#include <acexcep.h>
/*****************************************************************************
* Types & Defines
*****************************************************************************/
/*
* Output Flags (Debug):
* ---------------------
*/
#define BM_PRINT_ALL (0x00000000)
#define BM_PRINT_GROUP (0x00000001)
#define BM_PRINT_LINKAGE (0x00000002)
#define BM_PRINT_IDENTIFICATION (0x00000004)
#define BM_PRINT_POWER (0x00000008)
#define BM_PRINT_PRESENT (0x00000010)
/*
* BM_COMMAND:
* -----------
*/
typedef u32 BM_COMMAND;
#define BM_COMMAND_UNKNOWN ((BM_COMMAND) 0x00)
#define BM_COMMAND_GET_POWER_STATE ((BM_COMMAND) 0x01)
#define BM_COMMAND_SET_POWER_STATE ((BM_COMMAND) 0x02)
#define BM_COMMAND_DEVICE_SPECIFIC ((BM_COMMAND) 0x80)
/*
* BM_NOTIFY:
* ----------
* Standard ACPI notification values, from section 5.6.3 of the ACPI 2.0
* specification. Note that the Bus Manager internally handles all
* standard ACPI notifications -- driver modules are never sent these
* values (see "Bus Manager Notifications", below).
*/
typedef u32 BM_NOTIFY;
#define BM_NOTIFY_BUS_CHECK ((BM_NOTIFY) 0x00)
#define BM_NOTIFY_DEVICE_CHECK ((BM_NOTIFY) 0x01)
#define BM_NOTIFY_DEVICE_WAKE ((BM_NOTIFY) 0x02)
#define BM_NOTIFY_EJECT_REQUEST ((BM_NOTIFY) 0x03)
#define BM_NOTIFY_DEVICE_CHECK_LIGHT ((BM_NOTIFY) 0x04)
#define BM_NOTIFY_FREQUENCY_MISMATCH ((BM_NOTIFY) 0x05)
#define BM_NOTIFY_BUS_MODE_MISMATCH ((BM_NOTIFY) 0x06)
#define BM_NOTIFY_POWER_FAULT ((BM_NOTIFY) 0x07)
/*
* These are a higher-level abstraction of ACPI notifications, intended
* for consumption by driver modules to facilitate Pn_p.
*/
#define BM_NOTIFY_UNKNOWN ((BM_NOTIFY) 0x00)
#define BM_NOTIFY_DEVICE_ADDED ((BM_NOTIFY) 0x01)
#define BM_NOTIFY_DEVICE_REMOVED ((BM_NOTIFY) 0x02)
/*
* BM_HANDLE:
* ----------
*/
typedef u32 BM_HANDLE;
#define BM_HANDLE_ROOT ((BM_HANDLE) 0x00000000)
#define BM_HANDLE_UNKNOWN ((BM_HANDLE) 0xFFFFFFFF)
#define BM_HANDLES_MAX 100
/*
* BM_HANDLE_LIST:
* ---------------
*/
typedef struct
{
u32 count;
BM_HANDLE handles[BM_HANDLES_MAX];
} BM_HANDLE_LIST;
/*
* BM_DEVICE_TYPE:
* ---------------
*/
typedef u32 BM_DEVICE_TYPE;
#define BM_TYPE_UNKNOWN ((BM_DEVICE_TYPE) 0x00000000)
#define BM_TYPE_SYSTEM ((BM_DEVICE_TYPE) 0x00000001)
#define BM_TYPE_SCOPE ((BM_DEVICE_TYPE) 0x00000002)
#define BM_TYPE_PROCESSOR ((BM_DEVICE_TYPE) 0x00000003)
#define BM_TYPE_THERMAL_ZONE ((BM_DEVICE_TYPE) 0x00000004)
#define BM_TYPE_POWER_RESOURCE ((BM_DEVICE_TYPE) 0x00000005)
#define BM_TYPE_DEVICE ((BM_DEVICE_TYPE) 0x00000006)
#define BM_TYPE_FIXED_BUTTON ((BM_DEVICE_TYPE) 0x00000007)
/*
* BM_DEVICE_UID:
* --------------
*/
typedef char BM_DEVICE_UID[9];
#define BM_UID_UNKNOWN '0'
/*
* BM_DEVICE_HID:
* --------------
*/
typedef char BM_DEVICE_HID[9];
#define BM_HID_UNKNOWN '\0'
#define BM_HID_POWER_BUTTON "PNP0C0C"
#define BM_HID_SLEEP_BUTTON "PNP0C0E"
/*
* BM_DEVICE_ADR:
* --------------
*/
typedef u32 BM_DEVICE_ADR;
#define BM_ADDRESS_UNKNOWN 0
/*
* BM_DEVICE_FLAGS:
* ----------------
* The encoding of BM_DEVICE_FLAGS is illustrated below.
* Note that a set bit (1) indicates the property is TRUE
* (e.g. if bit 0 is set then the device has dynamic status).
* +--+------------+-+-+-+-+-+-+-+
* |31| Bits 30:7 |6|5|4|3|2|1|0|
* +--+------------+-+-+-+-+-+-+-+
* | | | | | | | | |
* | | | | | | | | +- Dynamic status?
* | | | | | | | +--- Identifiable?
* | | | | | | +----- Configurable?
* | | | | | +------- Power Control?
* | | | | +--------- Ejectable?
* | | | +----------- Docking Station?
* | | +------------- Fixed-Feature?
* | +-------------------- <Reserved>
* +---------------------------- Driver Control?
*
* Dynamic status: Device has a _STA object.
* Identifiable: Device has a _HID and/or _ADR and possibly other
* identification objects defined.
* Configurable: Device has a _CRS and possibly other configuration
* objects defined.
* Power Control: Device has a _PR0 and/or _PS0 and possibly other
* power management objects defined.
* Ejectable: Device has an _EJD and/or _EJx and possibly other
* dynamic insertion/removal objects defined.
* Docking Station: Device has a _DCK object defined.
* Fixed-Feature: Device does not exist in the namespace; was
* enumerated as a fixed-feature (e.g. power button).
* Driver Control: A driver has been installed for this device.
*/
typedef u32 BM_DEVICE_FLAGS;
#define BM_FLAGS_UNKNOWN ((BM_DEVICE_FLAGS) 0x00000000)
#define BM_FLAGS_DYNAMIC_STATUS ((BM_DEVICE_FLAGS) 0x00000001)
#define BM_FLAGS_IDENTIFIABLE ((BM_DEVICE_FLAGS) 0x00000002)
#define BM_FLAGS_CONFIGURABLE ((BM_DEVICE_FLAGS) 0x00000004)
#define BM_FLAGS_POWER_CONTROL ((BM_DEVICE_FLAGS) 0x00000008)
#define BM_FLAGS_EJECTABLE ((BM_DEVICE_FLAGS) 0x00000010)
#define BM_FLAGS_DOCKING_STATION ((BM_DEVICE_FLAGS) 0x00000020)
#define BM_FLAGS_FIXED_FEATURE ((BM_DEVICE_FLAGS) 0x00000040)
#define BM_FLAGS_DRIVER_CONTROL ((BM_DEVICE_FLAGS) 0x80000000)
/*
* Device PM Flags:
* ----------------
* +-----------+-+-+-+-+-+-+-+
* | Bits 31:7 |6|5|4|3|2|1|0|
* +-----------+-+-+-+-+-+-+-+
* | | | | | | | |
* | | | | | | | +- D0 Support?
* | | | | | | +--- D1 Support?
* | | | | | +----- D2 Support?
* | | | | +------- D3 Support?
* | | | +--------- Power State Queriable?
* | | +----------- Inrush Current?
* | +------------- Wake Capable?
* +-------------------- <Reserved>
*
* D0-D3 Support: Device supports corresponding Dx state.
* Power State: Device has a _PSC (current power state) object defined.
* Inrush Current: Device has an _IRC (inrush current) object defined.
* Wake Capable: Device has a _PRW (wake-capable) object defined.
*/
#define BM_FLAGS_D0_SUPPORT ((BM_DEVICE_FLAGS) 0x00000001)
#define BM_FLAGS_D1_SUPPORT ((BM_DEVICE_FLAGS) 0x00000002)
#define BM_FLAGS_D2_SUPPORT ((BM_DEVICE_FLAGS) 0x00000004)
#define BM_FLAGS_D3_SUPPORT ((BM_DEVICE_FLAGS) 0x00000008)
#define BM_FLAGS_POWER_STATE ((BM_DEVICE_FLAGS) 0x00000010)
#define BM_FLAGS_INRUSH_CURRENT ((BM_DEVICE_FLAGS) 0x00000020)
#define BM_FLAGS_WAKE_CAPABLE ((BM_DEVICE_FLAGS) 0x00000040)
/*
* BM_DEVICE_STATUS:
* -----------------
* The encoding of BM_DEVICE_STATUS is illustrated below.
* Note that a set bit (1) indicates the property is TRUE
* (e.g. if bit 0 is set then the device is present).
* +-----------+-+-+-+-+-+
* | Bits 31:4 |4|3|2|1|0|
* +-----------+-+-+-+-+-+
* | | | | | |
* | | | | | +- Present?
* | | | | +--- Enabled?
* | | | +----- Show in UI?
* | | +------- Functioning?
* | +--------- Battery Present?
* +---------------- <Reserved>
*/
typedef u32 BM_DEVICE_STATUS;
#define BM_STATUS_UNKNOWN ((BM_DEVICE_STATUS) 0x00000000)
#define BM_STATUS_PRESENT ((BM_DEVICE_STATUS) 0x00000001)
#define BM_STATUS_ENABLED ((BM_DEVICE_STATUS) 0x00000002)
#define BM_STATUS_SHOW_UI ((BM_DEVICE_STATUS) 0x00000004)
#define BM_STATUS_FUNCTIONING ((BM_DEVICE_STATUS) 0x00000008)
#define BM_STATUS_BATTERY_PRESENT ((BM_DEVICE_STATUS) 0x00000010)
#define BM_STATUS_DEFAULT ((BM_DEVICE_STATUS) 0x0000000F)
/*
* BM_POWER_STATE:
* ---------------
*/
typedef u32 BM_POWER_STATE;
/*
* BM_DEVICE_ID:
* -------------
*/
typedef struct
{
BM_DEVICE_TYPE type;
BM_DEVICE_UID uid;
BM_DEVICE_HID hid;
BM_DEVICE_ADR adr;
} BM_DEVICE_ID;
/*
* BM_DEVICE_POWER:
* ----------------
* Structure containing basic device power management information.
*/
typedef struct
{
BM_DEVICE_FLAGS flags;
BM_POWER_STATE state;
BM_DEVICE_FLAGS dx_supported[ACPI_S_STATE_COUNT];
} BM_DEVICE_POWER;
/*
* BM_DEVICE:
* ----------
*/
typedef struct
{
BM_HANDLE handle;
acpi_handle acpi_handle;
BM_DEVICE_FLAGS flags;
BM_DEVICE_STATUS status;
BM_DEVICE_ID id;
BM_DEVICE_POWER power;
} BM_DEVICE;
/*
* BM_SEARCH:
* ----------
* Structure used for searching the ACPI Bus Manager's device hierarchy.
*/
typedef struct
{
BM_DEVICE_ID criteria;
BM_HANDLE_LIST results;
} BM_SEARCH;
/*
* BM_REQUEST:
* -----------
* Structure used for sending requests to/through the ACPI Bus Manager.
*/
typedef struct
{
acpi_status status;
BM_COMMAND command;
BM_HANDLE handle;
acpi_buffer buffer;
} BM_REQUEST;
/*
* Driver Registration:
* --------------------
*/
/* Driver Context */
typedef void * BM_DRIVER_CONTEXT;
/* Notification Callback Function */
typedef
acpi_status (*BM_DRIVER_NOTIFY) (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
BM_DRIVER_CONTEXT *context);
/* Request Callback Function */
typedef
acpi_status (*BM_DRIVER_REQUEST) (
BM_REQUEST *request,
BM_DRIVER_CONTEXT context);
/* Driver Registration */
typedef struct
{
BM_DRIVER_NOTIFY notify;
BM_DRIVER_REQUEST request;
BM_DRIVER_CONTEXT context;
} BM_DRIVER;
/*
* BM_NODE:
* --------
* Structure used to maintain the device hierarchy.
*/
typedef struct _BM_NODE
{
BM_DEVICE device;
BM_DRIVER driver;
struct _BM_NODE *parent;
struct _BM_NODE *next;
struct
{
struct _BM_NODE *head;
struct _BM_NODE *tail;
} scope;
} BM_NODE;
/*
* BM_NODE_LIST:
* -------------
* Structure used to maintain an array of node pointers.
*/
typedef struct
{
u32 count;
BM_NODE *nodes[BM_HANDLES_MAX];
} BM_NODE_LIST;
/*****************************************************************************
* Macros
*****************************************************************************/
/*
* Device Presence:
* ----------------
* Note that status (_STA) means something different for power resources
* (they're assumed to always be present).
*/
#define BM_DEVICE_PRESENT(d) ((d->id.type!=BM_TYPE_POWER_RESOURCE)?(d->status & BM_STATUS_PRESENT):TRUE)
#define BM_NODE_PRESENT(n) ((n->device.id.type!=BM_TYPE_POWER_RESOURCE)?(n->device.status & BM_STATUS_PRESENT):TRUE)
/*
* Device Flags:
* -------------
*/
#define BM_IS_DRIVER_CONTROL(d) (d->flags & BM_FLAGS_DRIVER_CONTROL)
#define BM_IS_POWER_CONTROL(d) (d->flags & BM_FLAGS_POWER_CONTROL)
/*
* Device Power Flags:
* -------------------
*/
#define BM_IS_POWER_STATE(d) (d->power.flags & BM_FLAGS_POWER_STATE)
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
/* bm.c */
acpi_status
bm_initialize (void);
acpi_status
bm_terminate (void);
acpi_status
bm_get_status (
BM_DEVICE *device);
acpi_status
bm_get_handle (
acpi_handle acpi_handle,
BM_HANDLE *device_handle);
acpi_status
bm_get_node (
BM_HANDLE device_handle,
acpi_handle acpi_handle,
BM_NODE **node);
/* bmsearch.c */
acpi_status
bm_search(
BM_HANDLE device_handle,
BM_DEVICE_ID *criteria,
BM_HANDLE_LIST *results);
/* bmnotify.c */
void
bm_notify (
acpi_handle acpi_handle,
u32 notify_value,
void *context);
/* bm_request.c */
acpi_status
bm_request (
BM_REQUEST *request_info);
/* bmdriver.c */
acpi_status
bm_get_device_power_state (
BM_HANDLE device_handle,
BM_POWER_STATE *state);
acpi_status
bm_set_device_power_state (
BM_HANDLE device_handle,
BM_POWER_STATE state);
acpi_status
bm_get_device_status (
BM_HANDLE device_handle,
BM_DEVICE_STATUS *device_status);
acpi_status
bm_get_device_info (
BM_HANDLE device_handle,
BM_DEVICE **device_info);
acpi_status
bm_get_device_context (
BM_HANDLE device_handle,
BM_DRIVER_CONTEXT *context);
acpi_status
bm_register_driver (
BM_DEVICE_ID *criteria,
BM_DRIVER *driver);
acpi_status
bm_unregister_driver (
BM_DEVICE_ID *criteria,
BM_DRIVER *driver);
/* bmpm.c */
acpi_status
bm_get_pm_capabilities (
BM_NODE *node);
acpi_status
bm_get_power_state (
BM_NODE *node);
acpi_status
bm_set_power_state (
BM_NODE *node,
BM_POWER_STATE target_state);
/* bmpower.c */
acpi_status
bm_pr_initialize (void);
acpi_status
bm_pr_terminate (void);
/* bmutils.c */
acpi_status
bm_cast_buffer (
acpi_buffer *buffer,
void **pointer,
u32 length);
acpi_status
bm_copy_to_buffer (
acpi_buffer *buffer,
void *data,
u32 length);
acpi_status
bm_extract_package_data (
acpi_object *package,
acpi_buffer *format,
acpi_buffer *buffer);
acpi_status
bm_evaluate_object (
acpi_handle acpi_handle,
acpi_string pathname,
acpi_object_list *arguments,
acpi_buffer *buffer);
acpi_status
bm_evaluate_simple_integer (
acpi_handle acpi_handle,
acpi_string pathname,
u32 *data);
acpi_status
bm_evaluate_reference_list (
acpi_handle acpi_handle,
acpi_string pathname,
BM_HANDLE_LIST *reference_list);
/* ACPI Bus Driver OSL */
acpi_status
bm_osl_generate_event (
BM_HANDLE device_handle,
char *device_type,
char *device_instance,
u32 event_type,
u32 event_data);
#endif /* __BM_H__ */
/*****************************************************************************
*
* Module name: bmpower.h
* $Revision: 9 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __BMPOWER_H__
#define __BMPOWER_H__
#include "bm.h"
/*****************************************************************************
* Types & Defines
*****************************************************************************/
/*
* BM_POWER_RESOURCE:
* ------------------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
BM_POWER_STATE system_level;
u32 resource_order;
BM_POWER_STATE state;
u32 reference_count;
} BM_POWER_RESOURCE;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
/* bmpower.c */
acpi_status
bm_pr_initialize (void);
acpi_status
bm_pr_terminate (void);
acpi_status
bm_pr_list_get_state (
BM_HANDLE_LIST *resource_list,
BM_POWER_STATE *power_state);
acpi_status
bm_pr_list_transition (
BM_HANDLE_LIST *current_list,
BM_HANDLE_LIST *target_list);
#endif /* __BMPOWER_H__ */
/******************************************************************************
*
* Module Name: bn.h
* $Revision: 12 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __BN_H__
#define __BN_H__
#include <actypes.h>
#include <acexcep.h>
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
/*
* Notifications:
* ---------------------
*/
#define BN_NOTIFY_STATUS_CHANGE ((BM_NOTIFY) 0x80)
/*
* Types:
* ------
*/
#define BN_TYPE_POWER_BUTTON 0x01
#define BN_TYPE_POWER_BUTTON_FIXED 0x02
#define BN_TYPE_SLEEP_BUTTON 0x03
#define BN_TYPE_SLEEP_BUTTON_FIXED 0x04
#define BN_TYPE_LID_SWITCH 0x05
/*
* Hardware IDs:
* -------------
* TBD: Power and Sleep button HIDs also exist in <bm.h>. Should all
* HIDs (ACPI well-known devices) exist in one place (e.g.
* acpi_hid.h)?
*/
#define BN_HID_POWER_BUTTON "PNP0C0C"
#define BN_HID_SLEEP_BUTTON "PNP0C0E"
#define BN_HID_LID_SWITCH "PNP0C0D"
/*
* Device Context:
* ---------------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
u32 type;
} BN_CONTEXT;
/******************************************************************************
* Function Prototypes
*****************************************************************************/
acpi_status
bn_initialize (void);
acpi_status
bn_terminate (void);
acpi_status
bn_notify_fixed (
void *context);
acpi_status
bn_notify (
u32 notify_type,
u32 device,
void **context);
acpi_status
bn_request(
BM_REQUEST *request_info,
void *context);
/* Button OSL */
acpi_status
bn_osl_add_device (
BN_CONTEXT *button);
acpi_status
bn_osl_remove_device (
BN_CONTEXT *button);
acpi_status
bn_osl_generate_event (
u32 event,
BN_CONTEXT *button);
#endif /* __BN_H__ */
/******************************************************************************
*
* Module Name: bt.h
* $Revision: 18 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __BT_H__
#define __BT_H__
#include <actypes.h>
#include <acexcep.h>
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
/*! [Begin] no source code translation */
#define BT_UNKNOWN 0xFFFFFFFF
#define BT_POWER_UNITS_DEFAULT "?"
#define BT_POWER_UNITS_WATTS "mW"
#define BT_POWER_UNITS_AMPS "mA"
/*! [End] no source code translation !*/
/*
* Battery Notifications:
* ----------------------
*/
#define BT_NOTIFY_STATUS_CHANGE ((BM_NOTIFY) 0x80)
#define BT_NOTIFY_INFORMATION_CHANGE ((BM_NOTIFY) 0x81)
/*
* Hardware IDs:
* -------------
*/
#define BT_HID_CM_BATTERY "PNP0C0A"
/*
* BT_CM_BATTERY_INFO:
* -------------------
*/
typedef struct
{
acpi_integer power_unit;
acpi_integer design_capacity;
acpi_integer last_full_capacity;
acpi_integer battery_technology;
acpi_integer design_voltage;
acpi_integer design_capacity_warning;
acpi_integer design_capacity_low;
acpi_integer battery_capacity_granularity_1;
acpi_integer battery_capacity_granularity_2;
acpi_string model_number;
acpi_string serial_number;
acpi_string battery_type;
acpi_string oem_info;
} BT_BATTERY_INFO;
/*
* BT_CM_BATTERY_STATUS:
* ---------------------
*/
typedef struct
{
acpi_integer state;
acpi_integer present_rate;
acpi_integer remaining_capacity;
acpi_integer present_voltage;
} BT_BATTERY_STATUS;
/*
* BT_CONTEXT:
* -----------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
char uid[9];
acpi_string power_units;
u8 is_present;
} BT_CONTEXT;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
/* bt.c */
acpi_status
bt_initialize (void);
acpi_status
bt_terminate (void);
acpi_status
bt_notify (
u32 notify_type,
u32 device,
void **context);
acpi_status
bt_request(
BM_REQUEST *request_info,
void *context);
acpi_status
bt_get_status (
BT_CONTEXT *battery,
BT_BATTERY_STATUS **battery_status);
acpi_status
bt_get_info (
BT_CONTEXT *battery,
BT_BATTERY_INFO **battery_info);
/* Battery OSL */
acpi_status
bt_osl_add_device (
BT_CONTEXT *battery);
acpi_status
bt_osl_remove_device (
BT_CONTEXT *battery);
acpi_status
bt_osl_generate_event (
u32 event,
BT_CONTEXT *battery);
#endif /* __BT_H__ */
/*****************************************************************************
*
* Module Name: ec.h
* $Revision: 19 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __EC_H__
#define __EC_H__
#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <actypes.h>
#include <acexcep.h>
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
#define EC_DEFAULT_TIMEOUT 1000 /* 1 second */
#define EC_GPE_UNKNOWN 0xFFFFFFFF
#define EC_PORT_UNKNOWN 0x00000000
#define EC_BURST_ENABLE_ACKNOWLEDGE 0x90
/*
* Commands:
* ---------
*/
typedef u8 EC_COMMAND;
#define EC_COMMAND_UNKNOWN ((EC_COMMAND) 0x00)
#define EC_COMMAND_READ ((EC_COMMAND) 0x80)
#define EC_COMMAND_WRITE ((EC_COMMAND) 0x81)
#define EC_COMMAND_QUERY ((EC_COMMAND) 0x84)
/*
* EC_STATUS:
* ----------
* The encoding of the EC status register is illustrated below.
* Note that a set bit (1) indicates the property is TRUE
* (e.g. if bit 0 is set then the output buffer is full).
* +-+-+-+-+-+-+-+-+
* |7|6|5|4|3|2|1|0|
* +-+-+-+-+-+-+-+-+
* | | | | | | | |
* | | | | | | | +- Output Buffer Full (OBF)?
* | | | | | | +--- Input Buffer Full (IBF)?
* | | | | | +----- <reserved>
* | | | | +------- data Register is command Byte?
* | | | +--------- Burst Mode Enabled?
* | | +----------- SCI event?
* | +------------- SMI event?
* +--------------- <Reserved>
*
*/
typedef u32 EC_STATUS;
#define EC_FLAG_OUTPUT_BUFFER ((EC_STATUS) 0x01)
#define EC_FLAG_INPUT_BUFFER ((EC_STATUS) 0x02)
#define EC_FLAG_BURST_MODE ((EC_STATUS) 0x10)
#define EC_FLAG_SCI ((EC_STATUS) 0x20)
/*
* EC_EVENT:
* ---------
*/
typedef u32 EC_EVENT;
#define EC_EVENT_UNKNOWN ((EC_EVENT) 0x00)
#define EC_EVENT_NONE ((EC_EVENT) 0x00)
#define EC_EVENT_OUTPUT_BUFFER_FULL ((EC_EVENT) 0x01)
#define EC_EVENT_INPUT_BUFFER_EMPTY ((EC_EVENT) 0x02)
#define EC_EVENT_SCI ((EC_EVENT) 0x03)
/*
* Hardware IDs:
* -------------
*/
#define EC_HID_EC "PNP0C09"
/*
* EC_REQUEST:
* -----------
*/
typedef struct
{
EC_COMMAND command;
u8 address;
u8 data;
} EC_REQUEST;
/*
* Device Context:
* ---------------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
u32 gpe_bit;
u32 status_port;
u32 command_port;
u32 data_port;
u32 use_global_lock;
u8 query_data;
acpi_handle mutex;
} EC_CONTEXT;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
/* ec.c */
acpi_status
ec_initialize(void);
acpi_status
ec_terminate(void);
acpi_status
ec_notify (
u32 notify_type,
u32 device,
void **context);
acpi_status
ec_request(
BM_REQUEST *request_info,
void *context);
/* ectransx.c */
acpi_status
ec_transaction (
EC_CONTEXT *ec,
EC_REQUEST *ec_request);
acpi_status
ec_io_read (
EC_CONTEXT *ec,
u32 io_port,
u8 *data,
EC_EVENT wait_event);
acpi_status
ec_io_write (
EC_CONTEXT *ec,
u32 io_port,
u8 data,
EC_EVENT wait_event);
/* ecgpe.c */
acpi_status
ec_install_gpe_handler (
EC_CONTEXT *ec);
acpi_status
ec_remove_gpe_handler (
EC_CONTEXT *ec);
/* ecspace.c */
acpi_status
ec_install_space_handler (
EC_CONTEXT *ec);
acpi_status
ec_remove_space_handler (
EC_CONTEXT *ec);
#endif /* __EC_H__ */
/******************************************************************************
*
* Module Name: processor.h
* $Revision: 13 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __PR_H__
#define __PR_H__
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
#define PR_MAX_POWER_STATES 4
#define PR_MAX_THROTTLE_STATES 8
#define PR_MAX_PERF_STATES 32
#define PR_MAX_C2_LATENCY 100
#define PR_MAX_C3_LATENCY 1000
/*
* Commands:
* ---------
*/
#define PR_COMMAND_GET_POWER_INFO ((BM_COMMAND) 0x80)
#define PR_COMMAND_SET_POWER_INFO ((BM_COMMAND) 0x81)
#define PR_COMMAND_GET_PERF_INFO ((BM_COMMAND) 0x82)
#define PR_COMMAND_GET_PERF_STATE ((BM_COMMAND) 0x83)
#define PR_COMMAND_SET_PERF_LIMIT ((BM_COMMAND) 0x84)
/*
* Notifications:
* --------------
*/
#define PR_NOTIFY_PERF_STATES ((BM_NOTIFY) 0x80)
#define PR_NOTIFY_POWER_STATES ((BM_NOTIFY) 0x81)
/*
* Performance Control:
* --------------------
*/
#define PR_PERF_DEC 0x00
#define PR_PERF_INC 0x01
#define PR_PERF_MAX 0xFF
/*
* Power States:
* -------------
*/
#define PR_C0 0x00
#define PR_C1 0x01
#define PR_C2 0x02
#define PR_C3 0x03
#define PR_C1_FLAG 0x01;
#define PR_C2_FLAG 0x02;
#define PR_C3_FLAG 0x04;
/*
* PR_CX_POLICY_VALUES:
* --------------------
*/
typedef struct
{
u32 time_threshold;
u32 count_threshold;
u32 bm_threshold;
u32 target_state;
u32 count;
} PR_CX_POLICY_VALUES;
/*
* PR_CX:
* ------
*/
typedef struct
{
u32 latency;
u32 utilization;
u8 is_valid;
PR_CX_POLICY_VALUES promotion;
PR_CX_POLICY_VALUES demotion;
} PR_CX;
/*
* PR_POWER:
* ---------
*/
typedef struct
{
ACPI_PHYSICAL_ADDRESS p_lvl2;
ACPI_PHYSICAL_ADDRESS p_lvl3;
u32 bm_activity;
u32 active_state;
u32 default_state;
u32 busy_metric;
u32 state_count;
PR_CX state[PR_MAX_POWER_STATES];
} PR_POWER;
/*
* PR_PERFORMANCE_STATE:
* ---------------------
*/
typedef struct
{
u32 performance;
u32 power;
} PR_PERFORMANCE_STATE;
/*
* PR_PERFORMANCE:
* ---------------
*/
typedef struct
{
u32 active_state;
u32 thermal_limit;
u32 power_limit;
u32 state_count;
PR_PERFORMANCE_STATE state[PR_MAX_PERF_STATES];
} PR_PERFORMANCE;
/*
* PR_PBLOCK:
* ----------
*/
typedef struct
{
u32 length;
ACPI_PHYSICAL_ADDRESS address;
} PR_PBLOCK;
/*
* PR_CONTEXT:
* -----------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
u32 uid;
PR_PBLOCK pblk;
PR_POWER power;
PR_PERFORMANCE performance;
} PR_CONTEXT;
/******************************************************************************
* Function Prototypes
*****************************************************************************/
/* processor.c */
acpi_status
pr_initialize(void);
acpi_status
pr_terminate(void);
acpi_status
pr_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context);
acpi_status
pr_request(
BM_REQUEST *request,
void *context);
/* prpower.c */
void
pr_power_idle (void);
acpi_status
pr_power_add_device (
PR_CONTEXT *processor);
acpi_status
pr_power_remove_device (
PR_CONTEXT *processor);
acpi_status
pr_power_initialize (void);
acpi_status
pr_power_terminate (void);
/* prperf.c */
acpi_status
pr_perf_get_state (
PR_CONTEXT *processor,
u32 *state);
acpi_status
pr_perf_set_state (
PR_CONTEXT *processor,
u32 state);
acpi_status
pr_perf_set_limit (
PR_CONTEXT *processor,
u32 limit);
acpi_status
pr_perf_add_device (
PR_CONTEXT *processor);
acpi_status
pr_perf_remove_device (
PR_CONTEXT *processor);
/* Processor Driver OSL */
acpi_status
pr_osl_add_device (
PR_CONTEXT *processor);
acpi_status
pr_osl_remove_device (
PR_CONTEXT *processor);
acpi_status
pr_osl_generate_event (
u32 event,
PR_CONTEXT *processor);
#endif /* __PR_H__ */
/*****************************************************************************
*
* Module Name: sm.h
* $Revision: 3 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __SM_H__
#define __SM_H__
#include <actypes.h>
#include <acexcep.h>
#include <bm.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
#define SM_MAX_SYSTEM_STATES 6 /* S0-S5 */
/*
* Device Context:
* ---------------
*/
typedef struct
{
BM_HANDLE device_handle;
acpi_handle acpi_handle;
u8 states[SM_MAX_SYSTEM_STATES];
} SM_CONTEXT;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
acpi_status
sm_initialize (void);
acpi_status
sm_terminate (void);
acpi_status
sm_notify (
u32 notify_type,
u32 device,
void **context);
acpi_status
sm_request(
BM_REQUEST *request_info,
void *context);
/* System Driver OSL */
acpi_status
sm_osl_add_device (
SM_CONTEXT *system);
acpi_status
sm_osl_remove_device (
SM_CONTEXT *system);
acpi_status
sm_osl_generate_event (
u32 event,
SM_CONTEXT *system);
#endif /* __SM_H__ */
/*****************************************************************************
*
* Module Name: tz.h
* $Revision: 24 $
*
*****************************************************************************/
/*
* 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
*/
#ifndef __TZ_H__
#define __TZ_H__
/* TBD: Linux-specific */
#include <linux/module.h>
#include <linux/timer.h>
#include <bm.h>
#include <pr.h>
/*****************************************************************************
* Types & Other Defines
*****************************************************************************/
#define TZ_MAX_THRESHOLDS 12 /* _AC0 through _AC9 + _CRT + _PSV */
#define TZ_MAX_ACTIVE_THRESHOLDS 10 /* _AC0 through _AC9 */
#define TZ_MAX_COOLING_DEVICES 10 /* TBD: Make size dynamic */
/*
* Notifications:
* --------------
*/
#define TZ_NOTIFY_TEMPERATURE_CHANGE ((BM_NOTIFY) 0x80)
#define TZ_NOTIFY_THRESHOLD_CHANGE ((BM_NOTIFY) 0x81)
#define TZ_NOTIFY_DEVICE_LISTS_CHANGE ((BM_NOTIFY) 0x82)
/*
* TZ_THRESHOLD_TYPE:
* ------------------
*/
typedef u32 TZ_THRESHOLD_TYPE;
#define TZ_THRESHOLD_UNKNOWN ((TZ_THRESHOLD_TYPE) 0x00)
#define TZ_THRESHOLD_CRITICAL ((TZ_THRESHOLD_TYPE) 0x01)
#define TZ_THRESHOLD_PASSIVE ((TZ_THRESHOLD_TYPE) 0x02)
#define TZ_THRESHOLD_ACTIVE ((TZ_THRESHOLD_TYPE) 0x03)
/*
* TZ_COOLING_STATE:
* -----------------
*/
typedef u32 TZ_COOLING_STATE;
#define TZ_COOLING_UNKNOWN ((TZ_COOLING_STATE) 0x00)
#define TZ_COOLING_ENABLED ((TZ_COOLING_STATE) 0x01)
#define TZ_COOLING_DISABLED ((TZ_COOLING_STATE) 0x02)
/*
* TZ_COOLING_MODE:
* ----------------
*/
typedef u32 TZ_COOLING_MODE;
#define TZ_COOLING_MODE_ACTIVE ((TZ_COOLING_MODE) 0x00)
#define TZ_COOLING_MODE_PASSIVE ((TZ_COOLING_MODE) 0x01)
/*
* Thermal State:
* --------------
* The encoding of TZ_STATE is illustrated below.
* Note that a set bit (1) indicates the property is TRUE
* (e.g. if bit 0 is set then the device has dynamic status).
* No bits set indicates an OK cooling state.
* +--+--+--+-----------+----------+
* |31|30|29| Bits 27:4 | Bits 3:0 |
* +--+--+--+-----------+----------+
* | | | | |
* | | | | +------ Active Index
* | | | +----------------- <reserved>
* | | +------------------------- Active
* | +---------------------------- Passive
* +------------------------------- Critical
*
* Active Index: Value representing the level of active cooling
* presently applied (e.g. 0=_AL0, 9=_AL9). Only
* valid when 'Active' is set.
* Active: If set, indicates that the system temperature
* has crossed at least one active threshold (_ALx).
* Passive: If set, indicates that the system temperature
* has crossed the passive threshold (_PSL).
* Passive: If set, indicates that the system temperature
* has crossed the critical threshold (_CRT).
*/
typedef u32 TZ_STATE;
#define TZ_STATE_OK ((TZ_STATE) 0x00000000)
#define TZ_STATE_HOT ((TZ_STATE) 0x10000000)
#define TZ_STATE_ACTIVE ((TZ_STATE) 0x20000000)
#define TZ_STATE_PASSIVE ((TZ_STATE) 0x40000000)
#define TZ_STATE_CRITICAL ((TZ_STATE) 0x80000000)
typedef struct {
u32 temperature;
} TZ_CRITICAL_THRESHOLD;
typedef struct {
u8 is_valid;
u32 temperature;
} TZ_HOT_THRESHOLD;
typedef struct {
u8 is_valid;
u32 temperature;
u32 tc1;
u32 tc2;
u32 tsp;
BM_HANDLE_LIST devices;
} TZ_PASSIVE_THRESHOLD;
typedef struct {
u8 is_valid;
u32 temperature;
TZ_COOLING_STATE cooling_state;
BM_HANDLE_LIST devices;
} TZ_ACTIVE_THRESHOLD;
typedef struct {
TZ_CRITICAL_THRESHOLD critical;
TZ_HOT_THRESHOLD hot;
TZ_PASSIVE_THRESHOLD passive;
TZ_ACTIVE_THRESHOLD active[TZ_MAX_ACTIVE_THRESHOLDS];
} TZ_THRESHOLDS;
/*
* TZ_POLICY:
* ---------
*/
typedef struct {
u32 temperature;
TZ_STATE state;
TZ_COOLING_MODE cooling_mode;
u32 polling_freq;
TZ_THRESHOLDS thresholds;
struct timer_list timer;
} TZ_POLICY;
/*
* TZ_CONTEXT:
* -----------
*/
typedef struct {
BM_HANDLE device_handle;
acpi_handle acpi_handle;
char uid[9];
TZ_POLICY policy;
} TZ_CONTEXT;
/*****************************************************************************
* Function Prototypes
*****************************************************************************/
/* tz.c */
acpi_status
tz_initialize (void);
acpi_status
tz_terminate (void);
acpi_status
tz_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
BM_DRIVER_CONTEXT *context);
acpi_status
tz_request (
BM_REQUEST *request,
BM_DRIVER_CONTEXT context);
acpi_status
tz_get_temperature (
TZ_CONTEXT *tz);
acpi_status
tz_get_thresholds (
TZ_CONTEXT *tz);
acpi_status
tz_set_cooling_preference (
TZ_CONTEXT *tz,
TZ_COOLING_MODE cooling_mode);
void
tz_print (
TZ_CONTEXT *tz);
/* tzpolicy.c */
acpi_status
tz_policy_add_device (
TZ_CONTEXT *tz);
acpi_status
tz_policy_remove_device (
TZ_CONTEXT *tz);
void
tz_policy_check (
void *context);
/* tz_osl.c */
acpi_status
tz_osl_add_device (
TZ_CONTEXT *tz);
acpi_status
tz_osl_remove_device (
TZ_CONTEXT *tz);
acpi_status
tz_osl_generate_event (
u32 event,
TZ_CONTEXT *tz);
#endif /* __TZ_H__ */
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
/*****************************************************************************
*
* Module Name: pr.c
* $Revision: 34 $
*
*****************************************************************************/
/*
* 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 <bm.h>
#include "pr.h"
#define _COMPONENT ACPI_PROCESSOR
MODULE_NAME ("pr")
/****************************************************************************
* Globals
****************************************************************************/
extern fadt_descriptor_rev2 acpi_fadt;
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: pr_print
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Prints out information on a specific thermal zone.
*
****************************************************************************/
void
pr_print (
PR_CONTEXT *processor)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
FUNCTION_TRACE("pr_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(processor->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic processor information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Processor[%02x]:[%p] uid[%02x] %s\n", processor->device_handle, processor->acpi_handle, processor->uid, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| power: %cC0 %cC1 %cC2[%d] %cC3[%d]\n", (processor->power.state[0].is_valid?'+':'-'), (processor->power.state[1].is_valid?'+':'-'), (processor->power.state[2].is_valid?'+':'-'), processor->power.state[2].latency, (processor->power.state[3].is_valid?'+':'-'), processor->power.state[3].latency));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| performance: states[%d]\n", processor->performance.state_count));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /* ACPI_DEBUG */
return;
}
/****************************************************************************
*
* FUNCTION: pr_add_device
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_add_device(
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
PR_CONTEXT *processor = NULL;
BM_DEVICE *device = NULL;
acpi_buffer buffer;
acpi_object acpi_object;
static u32 processor_count = 0;
FUNCTION_TRACE("pr_add_device");
if (!context || *context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
processor = acpi_os_callocate(sizeof(PR_CONTEXT));
if (!processor) {
return AE_NO_MEMORY;
}
processor->device_handle = device->handle;
processor->acpi_handle = device->acpi_handle;
/*
* Processor Block:
* ----------------
*/
memset(&acpi_object, 0, sizeof(acpi_object));
buffer.length = sizeof(acpi_object);
buffer.pointer = &acpi_object;
status = acpi_evaluate_object(processor->acpi_handle, NULL, NULL, &buffer);
if (ACPI_FAILURE(status)) {
goto end;
}
/*
* Processor ID:
* -------------
* TBD: We need to synchronize the processor ID values in ACPI
* with those of the APIC. For example, an IBM T20 has a
* proc_id value of '1', where the Linux value for the
* first CPU on this system is '0'. Since x86 CPUs are
* mapped 1:1 we can simply use a zero-based counter. Note
* that this assumes that processor objects are enumerated
* in the proper order.
*/
/* processor->uid = acpi_object.processor.proc_id; */
processor->uid = processor_count++;
processor->pblk.length = acpi_object.processor.pblk_length;
processor->pblk.address = acpi_object.processor.pblk_address;
status = pr_power_add_device(processor);
if (ACPI_FAILURE(status)) {
goto end;
}
status = pr_perf_add_device(processor);
if (ACPI_FAILURE(status)) {
goto end;
}
status = pr_osl_add_device(processor);
if (ACPI_FAILURE(status)) {
goto end;
}
*context = processor;
pr_print(processor);
end:
if (ACPI_FAILURE(status)) {
acpi_os_free(processor);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_remove_device
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_remove_device (
void **context)
{
acpi_status status = AE_OK;
PR_CONTEXT *processor= NULL;
FUNCTION_TRACE("pr_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
processor = (PR_CONTEXT*)(*context);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing processor device [%02x].\n", processor->device_handle));
pr_osl_remove_device(processor);
pr_perf_remove_device(processor);
pr_power_remove_device(processor);
acpi_os_free(processor);
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: pr_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("pr_initialize");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Initialize power (Cx state) policy.
*/
status = pr_power_initialize();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Register driver for processor devices.
*/
criteria.type = BM_TYPE_PROCESSOR;
driver.notify = &pr_notify;
driver.request = &pr_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("pr_terminate");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Terminate power (Cx state) policy.
*/
status = pr_power_terminate();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Unegister driver for processor devices.
*/
criteria.type = BM_TYPE_PROCESSOR;
driver.notify = &pr_notify;
driver.request = &pr_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_notify
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
PR_CONTEXT *processor = NULL;
FUNCTION_TRACE("pr_notify");
processor = (PR_CONTEXT*)*context;
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = pr_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = pr_remove_device(context);
break;
case PR_NOTIFY_PERF_STATES:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Performance states change event detected on processor [%02x].\n", device_handle));
/* TBD: Streamline (this is simple but overkill). */
status = pr_perf_remove_device(processor);
if (ACPI_SUCCESS(status)) {
status = pr_perf_add_device(processor);
}
if (ACPI_SUCCESS(status)) {
status = pr_osl_generate_event(notify_type,
(processor));
}
break;
case PR_NOTIFY_POWER_STATES:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Power states change event detected on processor [%02x].\n", device_handle));
/* TBD: Streamline (this is simple but overkill). */
status = pr_power_remove_device(processor);
if (ACPI_SUCCESS(status)) {
status = pr_power_add_device(processor);
}
if (ACPI_SUCCESS(status)) {
status = pr_osl_generate_event(notify_type,
(processor));
}
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_request
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
PR_CONTEXT *processor = NULL;
FUNCTION_TRACE("pr_request");
/*
* Must have a valid request structure and context.
*/
if (!request || !context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
processor = (PR_CONTEXT*)context;
/*
* Handle request:
* ---------------
*/
switch (request->command) {
case PR_COMMAND_GET_POWER_INFO:
status = bm_copy_to_buffer(&(request->buffer),
&(processor->power), sizeof(PR_POWER));
break;
case PR_COMMAND_SET_POWER_INFO:
{
PR_POWER *power_info = NULL;
u32 i = 0;
status = bm_cast_buffer(&(request->buffer),
(void**)&power_info, sizeof(PR_POWER));
if (ACPI_SUCCESS(status)) {
for (i=0; i<processor->power.state_count; i++) {
MEMCPY(&(processor->power.state[i].promotion),
&(power_info->state[i].promotion),
sizeof(PR_CX_POLICY_VALUES));
MEMCPY(&(processor->power.state[i].demotion),
&(power_info->state[i].demotion),
sizeof(PR_CX_POLICY_VALUES));
}
}
}
break;
case PR_COMMAND_GET_PERF_INFO:
status = bm_copy_to_buffer(&(request->buffer),
&(processor->performance), sizeof(PR_PERFORMANCE));
break;
case PR_COMMAND_GET_PERF_STATE:
status = bm_copy_to_buffer(&(request->buffer),
&(processor->performance.active_state), sizeof(u32));
break;
case PR_COMMAND_SET_PERF_LIMIT:
{
u32 *limit = NULL;
status = bm_cast_buffer(&(request->buffer),
(void**)&limit, sizeof(u32));
if (ACPI_SUCCESS(status)) {
status = pr_perf_set_limit(processor, *limit);
}
}
break;
default:
status = AE_SUPPORT;
break;
}
request->status = status;
return_ACPI_STATUS(status);
}
/******************************************************************************
*
* Module Name: pr_osl.c
* $Revision: 21 $
*
*****************************************************************************/
/*
* 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/pci.h>
#include <acpi.h>
#include <bm.h>
#include "pr.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - IA32 Processor Driver");
MODULE_LICENSE("GPL");
#define PR_PROC_ROOT "processor"
#define PR_PROC_STATUS "status"
#define PR_PROC_INFO "info"
extern struct proc_dir_entry *bm_proc_root;
static struct proc_dir_entry *pr_proc_root = NULL;
extern unsigned short acpi_piix4_bmisx;
/****************************************************************************
*
* FUNCTION: pr_osl_proc_read_status
*
****************************************************************************/
static int
pr_osl_proc_read_status (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
PR_CONTEXT *processor = NULL;
char *p = page;
int len = 0;
if (!context || (off != 0)) {
goto end;
}
processor = (PR_CONTEXT*)context;
p += sprintf(p, "Bus Mastering Activity: %08x\n",
processor->power.bm_activity);
p += sprintf(p, "C-State Utilization: C1[%d] C2[%d] C3[%d]\n",
processor->power.state[PR_C1].utilization,
processor->power.state[PR_C2].utilization,
processor->power.state[PR_C3].utilization);
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: pr_osl_proc_read_info
*
****************************************************************************/
static int
pr_osl_proc_read_info (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
PR_CONTEXT *processor = NULL;
char *p = page;
int len = 0;
if (!context || (off != 0)) {
goto end;
}
processor = (PR_CONTEXT*)context;
p += sprintf(p, "<TBD>\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: pr_osl_add_device
*
****************************************************************************/
acpi_status
pr_osl_add_device(
PR_CONTEXT *processor)
{
u32 i = 0;
struct proc_dir_entry *proc_entry = NULL, *proc;
char processor_uid[16];
if (!processor) {
return(AE_BAD_PARAMETER);
}
printk("Processor[%x]:", processor->uid);
for (i=0; i<processor->power.state_count; i++) {
if (processor->power.state[i].is_valid) {
printk(" C%d", i);
}
}
if (processor->performance.state_count > 1)
printk(", %d throttling states", processor->performance.state_count);
if (acpi_piix4_bmisx && processor->power.state[3].is_valid)
printk(" (PIIX errata enabled)");
printk("\n");
sprintf(processor_uid, "%d", processor->uid);
proc_entry = proc_mkdir(processor_uid, pr_proc_root);
if (!proc_entry)
return(AE_ERROR);
proc = create_proc_read_entry(PR_PROC_STATUS, S_IFREG | S_IRUGO,
proc_entry, pr_osl_proc_read_status, (void*)processor);
if (!proc_entry)
return(AE_ERROR);
proc = create_proc_read_entry(PR_PROC_INFO, S_IFREG | S_IRUGO,
proc_entry, pr_osl_proc_read_info, (void*)processor);
if (!proc_entry)
return(AE_ERROR);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_osl_remove_device
*
****************************************************************************/
acpi_status
pr_osl_remove_device (
PR_CONTEXT *processor)
{
char proc_entry[64];
if (!processor) {
return(AE_BAD_PARAMETER);
}
sprintf(proc_entry, "%d/%s", processor->uid, PR_PROC_INFO);
remove_proc_entry(proc_entry, pr_proc_root);
sprintf(proc_entry, "%d/%s", processor->uid, PR_PROC_STATUS);
remove_proc_entry(proc_entry, pr_proc_root);
sprintf(proc_entry, "%d", processor->uid);
remove_proc_entry(proc_entry, pr_proc_root);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_osl_generate_event
*
****************************************************************************/
acpi_status
pr_osl_generate_event (
u32 event,
PR_CONTEXT *processor)
{
acpi_status status = AE_OK;
char processor_uid[16];
if (!processor) {
return(AE_BAD_PARAMETER);
}
switch (event) {
case PR_NOTIFY_PERF_STATES:
case PR_NOTIFY_POWER_STATES:
sprintf(processor_uid, "%d", processor->uid);
status = bm_osl_generate_event(processor->device_handle,
PR_PROC_ROOT, processor_uid, event, 0);
break;
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
* Errata Handling
****************************************************************************/
void acpi_pr_errata (void)
{
struct pci_dev *dev = NULL;
while ((dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID, dev))) {
switch (dev->device) {
case PCI_DEVICE_ID_INTEL_82801BA_8: /* PIIX4U4 */
case PCI_DEVICE_ID_INTEL_82801BA_9: /* PIIX4U3 */
case PCI_DEVICE_ID_INTEL_82451NX: /* PIIX4NX */
case PCI_DEVICE_ID_INTEL_82372FB_1: /* PIIX4U2 */
case PCI_DEVICE_ID_INTEL_82801AA_1: /* PIIX4U */
case PCI_DEVICE_ID_INTEL_82443MX_1: /* PIIX4E2 */
case PCI_DEVICE_ID_INTEL_82801AB_1: /* PIIX4E */
case PCI_DEVICE_ID_INTEL_82371AB: /* PIIX4 */
acpi_piix4_bmisx = pci_resource_start(dev, 4);
return;
}
}
return;
}
/****************************************************************************
*
* FUNCTION: pr_osl_init
*
* PARAMETERS: <none>
*
* RETURN: 0: Success
*
* DESCRIPTION: Module initialization.
*
****************************************************************************/
static int __init
pr_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
acpi_pr_errata();
pr_proc_root = proc_mkdir(PR_PROC_ROOT, bm_proc_root);
if (!pr_proc_root) {
status = AE_ERROR;
}
else {
status = pr_initialize();
if (ACPI_FAILURE(status)) {
remove_proc_entry(PR_PROC_ROOT, bm_proc_root);
}
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: pr_osl_cleanup
*
* PARAMETERS: <none>
*
* RETURN: <none>
*
* DESCRIPTION: Module cleanup.
*
****************************************************************************/
static void __exit
pr_osl_cleanup (void)
{
pr_terminate();
if (pr_proc_root) {
remove_proc_entry(PR_PROC_ROOT, bm_proc_root);
}
return;
}
module_init(pr_osl_init);
module_exit(pr_osl_cleanup);
/*****************************************************************************
*
* Module Name: prperf.c
* $Revision: 21 $
*
*****************************************************************************/
/*
* 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
*/
/*
* TBD: 1. Support ACPI 2.0 processor performance states (not just throttling).
* 2. Fully implement thermal -vs- power management limit control.
*/
#include <acpi.h>
#include <bm.h>
#include "pr.h"
#define _COMPONENT ACPI_PROCESSOR
MODULE_NAME ("prperf")
/****************************************************************************
* Globals
****************************************************************************/
extern fadt_descriptor_rev2 acpi_fadt;
const u32 POWER_OF_2[] = {1,2,4,8,16,32,64,128,256,512};
/****************************************************************************
*
* FUNCTION: pr_perf_get_frequency
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_perf_get_frequency (
PR_CONTEXT *processor,
u32 *frequency) {
acpi_status status = AE_OK;
FUNCTION_TRACE("pr_perf_get_frequency");
if (!processor || !frequency) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/* TBD: Generic method to calculate processor frequency. */
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_perf_get_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
/* TBD: Include support for _real_ performance states (not just throttling). */
acpi_status
pr_perf_get_state (
PR_CONTEXT *processor,
u32 *state)
{
u32 pblk_value = 0;
u32 duty_mask = 0;
u32 duty_cycle = 0;
FUNCTION_TRACE("pr_perf_get_state");
if (!processor || !state) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (processor->performance.state_count == 1) {
*state = 0;
return_ACPI_STATUS(AE_OK);
}
acpi_os_read_port(processor->pblk.address, &pblk_value, 32);
/*
* Throttling Enabled?
* -------------------
* If so, calculate the current throttling state, otherwise return
* '100% performance' (state 0).
*/
if (pblk_value & 0x00000010) {
duty_mask = processor->performance.state_count - 1;
duty_mask <<= acpi_fadt.duty_offset;
duty_cycle = pblk_value & duty_mask;
duty_cycle >>= acpi_fadt.duty_offset;
if (duty_cycle == 0) {
*state = 0;
}
else {
*state = processor->performance.state_count -
duty_cycle;
}
}
else {
*state = 0;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Processor [%02x] is at performance state [%d%%].\n", processor->device_handle, processor->performance.state[*state].performance));
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_perf_set_state
*
* PARAMETERS:
*
* RETURN: AE_OK
* AE_BAD_PARAMETER
* AE_BAD_DATA Invalid target throttling state.
*
* DESCRIPTION:
*
****************************************************************************/
/* TBD: Includes support for _real_ performance states (not just throttling). */
acpi_status
pr_perf_set_state (
PR_CONTEXT *processor,
u32 state)
{
u32 pblk_value = 0;
u32 duty_mask = 0;
u32 duty_cycle = 0;
u32 i = 0;
FUNCTION_TRACE ("pr_perf_set_state");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (state > (processor->performance.state_count - 1)) {
return_ACPI_STATUS(AE_BAD_DATA);
}
if ((state == processor->performance.active_state) ||
(processor->performance.state_count == 1)) {
return_ACPI_STATUS(AE_OK);
}
/*
* Calculate Duty Cycle/Mask:
* --------------------------
* Note that we don't support duty_cycle values that span bit 4.
*/
if (state) {
duty_cycle = processor->performance.state_count - state;
duty_cycle <<= acpi_fadt.duty_offset;
}
else {
duty_cycle = 0;
}
duty_mask = ~((u32)(processor->performance.state_count - 1));
for (i=0; i<acpi_fadt.duty_offset; i++) {
duty_mask <<= acpi_fadt.duty_offset;
duty_mask += 1;
}
/*
* Disable Throttling:
* -------------------
* Got to turn it off before you can change the duty_cycle value.
* Throttling is disabled by writing a 0 to bit 4.
*/
acpi_os_read_port(processor->pblk.address, &pblk_value, 32);
if (pblk_value & 0x00000010) {
pblk_value &= 0xFFFFFFEF;
acpi_os_write_port(processor->pblk.address, pblk_value, 32);
}
/*
* Set Duty Cycle:
* ---------------
* Mask off the old duty_cycle value, mask in the new.
*/
pblk_value &= duty_mask;
pblk_value |= duty_cycle;
acpi_os_write_port(processor->pblk.address, pblk_value, 32);
/*
* Enable Throttling:
* ------------------
* But only for non-zero (non-100% performance) states.
*/
if (state) {
pblk_value |= 0x00000010;
acpi_os_write_port(processor->pblk.address, pblk_value, 32);
}
processor->performance.active_state = state;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Processor [%02x] set to performance state [%d%%].\n", processor->device_handle, processor->performance.state[state].performance));
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_perf_set_limit
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_perf_set_limit (
PR_CONTEXT *processor,
u32 limit)
{
acpi_status status = AE_OK;
PR_PERFORMANCE *performance = NULL;
FUNCTION_TRACE ("pr_perf_set_limit");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
performance = &(processor->performance);
/*
* Set Limit:
* ----------
* TBD: Properly manage thermal and power limits (only set
* performance state iff...).
*/
switch (limit) {
case PR_PERF_DEC:
if (performance->active_state <
(performance->state_count-1)) {
status = pr_perf_set_state(processor,
(performance->active_state+1));
}
break;
case PR_PERF_INC:
if (performance->active_state > 0) {
status = pr_perf_set_state(processor,
(performance->active_state-1));
}
break;
case PR_PERF_MAX:
if (performance->active_state != 0) {
status = pr_perf_set_state(processor, 0);
}
break;
default:
return_ACPI_STATUS(AE_BAD_DATA);
break;
}
if (ACPI_SUCCESS(status)) {
performance->thermal_limit = performance->active_state;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Processor [%02x] thermal performance limit set to [%d%%].\n", processor->device_handle, processor->performance.state[performance->active_state].performance));
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: pr_perf_add_device
*
* PARAMETERS: processor Our processor-specific context.
*
* RETURN: AE_OK
* AE_BAD_PARAMETER
*
* DESCRIPTION: Calculates the number of throttling states and the state
* performance/power values.
*
****************************************************************************/
/* TBD: Support duty_cycle values that span bit 4. */
acpi_status
pr_perf_add_device (
PR_CONTEXT *processor)
{
acpi_status status = AE_OK;
u32 i = 0;
u32 performance_step = 0;
u32 percentage = 0;
FUNCTION_TRACE("pr_perf_add_device");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Valid PBLK?
* -----------
* For SMP it is common to have the first (boot) processor have a
* valid PBLK while all others do not -- which implies that
* throttling has system-wide effects (duty_cycle programmed into
* the chipset effects all processors).
*/
if ((processor->pblk.length < 6) || !processor->pblk.address) {
processor->performance.state_count = 1;
}
/*
* Valid Duty Offset/Width?
* ------------------------
* We currently only support duty_cycle values that fall within
* bits 0-3, as things get complicated when this value spans bit 4
* (the throttling enable/disable bit).
*/
else if ((acpi_fadt.duty_offset + acpi_fadt.duty_width) > 4) {
processor->performance.state_count = 1;
}
/*
* Compute State Count:
* --------------------
* The number of throttling states is computed as 2^duty_width,
* but limited by PR_MAX_THROTTLE_STATES. Note that a duty_width
* of zero results is one throttling state (100%).
*/
else {
processor->performance.state_count =
POWER_OF_2[acpi_fadt.duty_width];
}
if (processor->performance.state_count > PR_MAX_THROTTLE_STATES) {
processor->performance.state_count = PR_MAX_THROTTLE_STATES;
}
/*
* Compute State Values:
* ---------------------
* Note that clock throttling displays a linear power/performance
* relationship (at 50% performance the CPU will consume 50% power).
*/
performance_step = (1000 / processor->performance.state_count);
for (i=0; i<processor->performance.state_count; i++) {
percentage = (1000 - (performance_step * i))/10;
processor->performance.state[i].performance = percentage;
processor->performance.state[i].power = percentage;
}
/*
* Get Current State:
* ------------------
*/
status = pr_perf_get_state(processor, &(processor->performance.active_state));
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Set to Maximum Performance:
* ---------------------------
* We'll let subsequent policy (e.g. thermal/power) decide to lower
* performance if it so chooses, but for now crank up the speed.
*/
if (0 != processor->performance.active_state) {
status = pr_perf_set_state(processor, 0);
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_perf_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_perf_remove_device (
PR_CONTEXT *processor)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("pr_perf_remove_device");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&(processor->performance), 0, sizeof(PR_PERFORMANCE));
return_ACPI_STATUS(status);
}
/*****************************************************************************
*
* Module Name: prpower.c
* $Revision: 32 $
*
*****************************************************************************/
/*
* 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
*/
/* TBD: Linux specific */
#include <linux/sched.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <acpi.h>
#include <bm.h>
#include "pr.h"
#define _COMPONENT ACPI_PROCESSOR
MODULE_NAME ("prpower")
/****************************************************************************
* Globals
****************************************************************************/
extern FADT_DESCRIPTOR acpi_fadt;
static u32 last_idle_jiffies = 0;
static PR_CONTEXT *processor_list[NR_CPUS];
static void (*pr_pm_idle_save)(void) = NULL;
static u8 bm_control = 0;
/* Used for PIIX4 errata handling. */
unsigned short acpi_piix4_bmisx = 0;
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: pr_power_activate_state
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
pr_power_activate_state (
PR_CONTEXT *processor,
u32 next_state)
{
PROC_NAME("pr_power_activate_state");
if (!processor) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return;
}
processor->power.state[processor->power.active_state].promotion.count = 0;
processor->power.state[processor->power.active_state].demotion.count = 0;
/*
* Cleanup from old state.
*/
switch (processor->power.active_state) {
case PR_C3:
/* Disable bus master reload */
acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
BM_RLD, 0);
break;
}
/*
* Prepare to use new state.
*/
switch (next_state) {
case PR_C3:
/* Enable bus master reload */
acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
BM_RLD, 1);
break;
}
processor->power.active_state = next_state;
return;
}
/****************************************************************************
*
* FUNCTION: pr_power_idle
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
pr_power_idle (void)
{
PR_CX *c_state = NULL;
u32 next_state = 0;
u32 start_ticks, end_ticks, time_elapsed;
PR_CONTEXT *processor = NULL;
PROC_NAME("pr_power_idle");
processor = processor_list[smp_processor_id()];
if (!processor || processor->power.active_state == PR_C0) {
return;
}
next_state = processor->power.active_state;
/*
* Check OS Idleness:
* ------------------
* If the OS has been busy (hasn't called the idle handler in a while)
* then automatically demote to the default power state (e.g. C1).
*
* TBD: Optimize by having scheduler determine business instead
* of having us try to calculate it.
*/
if (processor->power.active_state != processor->power.default_state) {
if ((jiffies - last_idle_jiffies) >= processor->power.busy_metric) {
next_state = processor->power.default_state;
if (next_state != processor->power.active_state) {
pr_power_activate_state(processor, next_state);
}
}
}
disable();
/*
* Log BM Activity:
* ----------------
* Read BM_STS and record its value for later use by C3 policy.
* (Note that we save the BM_STS values for the last 32 cycles).
*/
if (bm_control) {
processor->power.bm_activity <<= 1;
if (acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK, BM_STS)) {
processor->power.bm_activity |= 1;
acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
BM_STS, 1);
}
else if (acpi_piix4_bmisx) {
/*
* PIIX4 Errata:
* -------------
* This code is a workaround for errata #18 "C3 Power State/
* BMIDE and Type-F DMA Livelock" from the July '01 PIIX4
* specification update. Note that BM_STS doesn't always
* reflect the true state of bus mastering activity; forcing
* us to manually check the BMIDEA bit of each IDE channel.
*/
if ((inb_p(acpi_piix4_bmisx + 0x02) & 0x01) ||
(inb_p(acpi_piix4_bmisx + 0x0A) & 0x01))
processor->power.bm_activity |= 1;
}
}
c_state = &(processor->power.state[processor->power.active_state]);
c_state->utilization++;
/*
* Sleep:
* ------
* Invoke the current Cx state to put the processor to sleep.
*/
switch (processor->power.active_state) {
case PR_C1:
/* Invoke C1 */
enable(); halt();
/*
* TBD: Can't get time duration while in C1, as resumes
* go to an ISR rather than here.
*/
time_elapsed = 0xFFFFFFFF;
break;
case PR_C2:
/* See how long we're asleep for */
acpi_get_timer(&start_ticks);
/* Invoke C2 */
acpi_os_read_port(processor->power.p_lvl2, NULL, 8);
/* Dummy op - must do something useless after P_LVL2 read */
acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK, BM_STS);
/* Compute time elapsed */
acpi_get_timer(&end_ticks);
/* Re-enable interrupts */
enable();
acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
break;
case PR_C3:
/* Disable bus master arbitration */
acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, ARB_DIS, 1);
/* See how long we're asleep for */
acpi_get_timer(&start_ticks);
/* Invoke C3 */
acpi_os_read_port(processor->power.p_lvl3, NULL, 8);
/* Dummy op - must do something useless after P_LVL3 read */
acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK, BM_STS);
/* Compute time elapsed */
acpi_get_timer(&end_ticks);
/* Enable bus master arbitration */
acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
ARB_DIS, 0);
/* Re-enable interrupts */
enable();
acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
break;
default:
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Attempt to use unsupported power state C%d.\n", processor->power.active_state));
enable();
break;
}
/*
* Promotion?
* ----------
* Track the number of successful sleeps (time asleep is greater
* than time_threshold) and promote when count_threshold is
* reached.
*/
if ((c_state->promotion.target_state) &&
(time_elapsed >= c_state->promotion.time_threshold)) {
c_state->promotion.count++;
c_state->demotion.count = 0;
if (c_state->promotion.count >= c_state->promotion.count_threshold) {
/*
* Bus Mastering Activity, if active and used
* by this state's promotion policy, prevents
* promotions from occuring.
*/
if (!bm_control || !(processor->power.bm_activity & c_state->promotion.bm_threshold))
next_state = c_state->promotion.target_state;
}
}
/*
* Demotion?
* ---------
* Track the number of shorts (time asleep is less than
* time_threshold) and demote when count_threshold is reached.
*/
if (c_state->demotion.target_state) {
if (time_elapsed < c_state->demotion.time_threshold) {
c_state->demotion.count++;
c_state->promotion.count = 0;
if (c_state->demotion.count >=
c_state->demotion.count_threshold) {
next_state = c_state->demotion.target_state;
}
}
/*
* Bus Mastering Activity, if active and used by this
* state's promotion policy, causes an immediate demotion
* to occur.
*/
if (bm_control && (processor->power.bm_activity & c_state->demotion.bm_threshold))
next_state = c_state->demotion.target_state;
}
/*
* New Cx State?
* -------------
* If we're going to start using a new Cx state we must clean up
* from the previous and prepare to use the new.
*/
if (next_state != processor->power.active_state) {
pr_power_activate_state(processor, next_state);
processor->power.active_state = processor->power.active_state;
}
/*
* Track OS Idleness:
* ------------------
* Record a jiffies timestamp to compute time elapsed between calls
* to the idle handler.
*/
last_idle_jiffies = jiffies;
return;
}
/*****************************************************************************
*
* FUNCTION: pr_power_set_default_policy
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Sets the default Cx state policy (OS idle handler). Our
* scheme is to promote quickly to C2 but more conservatively
* to C3. We're favoring C2 for its characteristics of low
* latency (quick response), good power savings, and ability
* to allow bus mastering activity.
*
* Note that Cx state policy is completely customizable, with
* the goal of having heuristics to alter policy dynamically.
*
****************************************************************************/
acpi_status
pr_power_set_default_policy (
PR_CONTEXT *processor)
{
FUNCTION_TRACE("pr_power_set_default_policy");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Busy Metric:
* ------------
* Used to determine when the OS has been busy and thus when
* policy should return to using the default Cx state (e.g. C1).
* On Linux we use the number of jiffies (scheduler quantums)
* that transpire between calls to the idle handler.
*
* TBD: Linux-specific.
*/
processor->power.busy_metric = 2;
/*
* C1:
* ---
* C1 serves as our default state. It must be valid.
*/
if (processor->power.state[PR_C1].is_valid) {
processor->power.active_state =
processor->power.default_state = PR_C1;
}
else {
processor->power.active_state =
processor->power.default_state = PR_C0;
return_ACPI_STATUS(AE_OK);
}
/*
* C2:
* ---
* Set default C1 promotion and C2 demotion policies.
*/
if (processor->power.state[PR_C2].is_valid) {
/*
* Promote from C1 to C2 anytime we're asleep in C1 for
* longer than two times the C2 latency (to amortize cost
* of transition). Demote from C2 to C1 anytime we're
* asleep in C2 for less than this time.
*/
processor->power.state[PR_C1].promotion.count_threshold = 10;
processor->power.state[PR_C1].promotion.time_threshold =
2 * processor->power.state[PR_C2].latency;
processor->power.state[PR_C1].promotion.target_state = PR_C2;
processor->power.state[PR_C2].demotion.count_threshold = 1;
processor->power.state[PR_C2].demotion.time_threshold =
2 * processor->power.state[PR_C2].latency;
processor->power.state[PR_C2].demotion.target_state = PR_C1;
}
/*
* C3:
* ---
* Set default C2 promotion and C3 demotion policies.
*/
if ((processor->power.state[PR_C2].is_valid) &&
(processor->power.state[PR_C3].is_valid)) {
/*
* Promote from C2 to C3 after 4 cycles of no bus
* mastering activity (while maintaining sleep time
* criteria). Demote immediately on a short or
* whenever bus mastering activity occurs.
*/
processor->power.state[PR_C2].promotion.count_threshold = 1;
processor->power.state[PR_C2].promotion.time_threshold =
2 * processor->power.state[PR_C3].latency;
processor->power.state[PR_C2].promotion.bm_threshold =
0x0000000F;
processor->power.state[PR_C2].promotion.target_state =
PR_C3;
processor->power.state[PR_C3].demotion.count_threshold = 1;
processor->power.state[PR_C3].demotion.time_threshold =
2 * processor->power.state[PR_C3].latency;
processor->power.state[PR_C3].demotion.bm_threshold =
0x0000000F;
processor->power.state[PR_C3].demotion.target_state =
PR_C2;
}
return_ACPI_STATUS(AE_OK);
}
/*****************************************************************************
*
* FUNCTION: pr_power_add_device
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
/*
* TBD: 1. PROC_C1 support.
* 2. Symmetric Cx state support (different Cx states supported
* by different CPUs results in lowest common denominator).
*/
acpi_status
pr_power_add_device (
PR_CONTEXT *processor)
{
FUNCTION_TRACE("pr_power_add_device");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* State Count:
* ------------
* Fixed at four (C0-C3). We use is_valid to determine whether or
* not a state actually gets used.
*/
processor->power.state_count = PR_MAX_POWER_STATES;
/*
* C0:
* ---
* C0 exists only as filler in our array. (Let's assume its valid!)
*/
processor->power.state[PR_C0].is_valid = TRUE;
/*
* C1:
* ---
* ACPI states that C1 must be supported by all processors
* with a latency so small that it can be ignored.
*
* TBD: What about PROC_C1 support?
*/
processor->power.state[PR_C1].is_valid = TRUE;
/*
* C2:
* ---
* We're only supporting C2 on UP systems with latencies <= 100us.
*
* TBD: Support for C2 on MP (P_LVL2_UP) -- I'm taking the
* conservative approach for now.
*/
processor->power.state[PR_C2].latency = acpi_fadt.plvl2_lat;
#ifdef CONFIG_SMP
if (smp_num_cpus == 1) {
#endif /*CONFIG_SMP*/
if (acpi_fadt.plvl2_lat <= PR_MAX_C2_LATENCY) {
processor->power.state[PR_C2].is_valid = TRUE;
processor->power.p_lvl2 = processor->pblk.address + 4;
}
#ifdef CONFIG_SMP
}
#endif /*CONFIG_SMP*/
/*
* C3:
* ---
* We're only supporting C3 on UP systems with latencies <= 1000us,
* and that include the ability to disable bus mastering while in
* C3 (ARB_DIS) but allows bus mastering requests to wake the system
* from C3 (BM_RLD). Note his method of maintaining cache coherency
* (disabling of bus mastering) cannot be used on SMP systems, and
* flushing caches (e.g. WBINVD) is simply too costly at this time.
*
* TBD: Support for C3 on MP -- I'm taking the conservative
* approach for now.
*/
processor->power.state[PR_C3].latency = acpi_fadt.plvl3_lat;
#ifdef CONFIG_SMP
if (smp_num_cpus == 1) {
#endif /*CONFIG_SMP*/
if ((acpi_fadt.plvl3_lat <= PR_MAX_C3_LATENCY) && bm_control) {
processor->power.state[PR_C3].is_valid = TRUE;
processor->power.p_lvl3 = processor->pblk.address + 5;
}
#ifdef CONFIG_SMP
}
#endif /*CONFIG_SMP*/
/*
* Set Default Policy:
* -------------------
* Now that we know which state are supported, set the default
* policy. Note that this policy can be changed dynamically
* (e.g. encourage deeper sleeps to conserve battery life when
* not on AC).
*/
pr_power_set_default_policy(processor);
/*
* Save Processor Context:
* -----------------------
* TBD: Enhance Linux idle handler to take processor context
* parameter.
*/
processor_list[processor->uid] = processor;
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_power_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_power_remove_device (
PR_CONTEXT *processor)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("pr_power_remove_device");
if (!processor) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
MEMSET(&(processor->power), 0, sizeof(PR_POWER));
processor_list[processor->uid] = NULL;
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: pr_power_initialize
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_power_initialize (void)
{
u32 i = 0;
FUNCTION_TRACE("pr_power_initialize");
/* TBD: Linux-specific. */
for (i=0; i<NR_CPUS; i++) {
processor_list[i] = NULL;
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Max CPUs[%d], this CPU[%d].\n", NR_CPUS, smp_processor_id()));
/* Only use C3 if we can control bus mastering. */
if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len)
bm_control = 1;
/*
* Install idle handler.
*
* TBD: Linux-specific (need OSL function).
*/
pr_pm_idle_save = pm_idle;
pm_idle = pr_power_idle;
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: pr_power_terminate
*
* PARAMETERS: <none>
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
pr_power_terminate (void)
{
FUNCTION_TRACE("pr_power_terminate");
/*
* Remove idle handler.
*
* TBD: Linux-specific (need OSL function).
*/
pm_idle = pr_pm_idle_save;
return_ACPI_STATUS(AE_OK);
}
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
/*****************************************************************************
*
* Module Name: sm.c
* $Revision: 20 $
*
*****************************************************************************/
/*
* 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);
}
/******************************************************************************
*
* Module Name: sm_osl.c
* $Revision: 16 $
*
*****************************************************************************/
/*
* 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");
MODULE_LICENSE("GPL");
#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);
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
/*****************************************************************************
*
* Module Name: tz.c
* $Revision: 44 $
*
*****************************************************************************/
/*
* 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 <bm.h>
#include "tz.h"
#define _COMPONENT ACPI_THERMAL
MODULE_NAME ("tz")
/****************************************************************************
* Globals
****************************************************************************/
extern int TZP;
/****************************************************************************
* Internal Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: tz_print
*
****************************************************************************/
void
tz_print (
TZ_CONTEXT *tz)
{
#ifdef ACPI_DEBUG
acpi_buffer buffer;
u32 i,j = 0;
TZ_THRESHOLDS *thresholds = NULL;
FUNCTION_TRACE("tz_print");
if (!tz)
return;
thresholds = &(tz->policy.thresholds);
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(tz->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
/*
* Print out basic thermal zone information.
*/
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| Thermal_zone[%02x]:[%p] %s\n", tz->device_handle, tz->acpi_handle, (char*)buffer.pointer));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| temperature[%d] state[%08x]\n", tz->policy.temperature, tz->policy.state));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| cooling_mode[%08x] polling_freq[%d]\n", tz->policy.cooling_mode, tz->policy.polling_freq));
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| critical[%d]\n", thresholds->critical.temperature));
if (thresholds->hot.is_valid)
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| hot[%d]\n", thresholds->hot.temperature));
if (thresholds->passive.is_valid) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| passive[%d]: tc1[%d] tc2[%d] tsp[%d]\n", thresholds->passive.temperature, thresholds->passive.tc1, thresholds->passive.tc2, thresholds->passive.tsp));
if (thresholds->passive.devices.count > 0) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| devices"));
for (j=0; (j<thresholds->passive.devices.count && j<10); j++) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "[%02x]", thresholds->passive.devices.handles[j]));
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "\n"));
}
}
for (i=0; i<TZ_MAX_ACTIVE_THRESHOLDS; i++) {
if (!thresholds->active[i].is_valid)
break;
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| active[%d]: index[%d]\n", thresholds->active[i].temperature, i));
if (thresholds->active[i].devices.count > 0) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "| devices"));
for (j=0; (j<thresholds->active[i].devices.count && j<10); j++) {
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "[%02x]", thresholds->active[i].devices.handles[j]));
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "\n"));
}
}
ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INFO, "+------------------------------------------------------------\n"));
acpi_os_free(buffer.pointer);
#endif /*ACPI_DEBUG*/
return;
}
/****************************************************************************
*
* FUNCTION: tz_get_temperaturee
*
****************************************************************************/
acpi_status
tz_get_temperature (
TZ_CONTEXT *tz)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("tz_get_temperature");
if (!tz) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Evaluate the _TMP method to get the current temperature.
*/
status = bm_evaluate_simple_integer(tz->acpi_handle, "_TMP", &(tz->policy.temperature));
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %d d_k\n", tz->policy.temperature));
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_set_cooling_preference
*
****************************************************************************/
acpi_status
tz_set_cooling_preference (
TZ_CONTEXT *tz,
TZ_COOLING_MODE cooling_mode)
{
acpi_status status = AE_OK;
acpi_object_list arg_list;
acpi_object arg0;
FUNCTION_TRACE("tz_set_cooling_preference");
if (!tz || ((cooling_mode != TZ_COOLING_MODE_ACTIVE) && (cooling_mode != TZ_COOLING_MODE_PASSIVE))) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Build the argument list, which simply consists of the current
* cooling preference.
*/
memset(&arg_list, 0, sizeof(acpi_object));
arg_list.count = 1;
arg_list.pointer = &arg0;
memset(&arg0, 0, sizeof(acpi_object));
arg0.type = ACPI_TYPE_INTEGER;
arg0.integer.value = cooling_mode;
/*
* Evaluate "_SCP" - setting the new cooling preference.
*/
status = acpi_evaluate_object(tz->acpi_handle, "_SCP", &arg_list, NULL);
if (ACPI_FAILURE(status)) {
tz->policy.cooling_mode = -1;
return_ACPI_STATUS(status);
}
tz->policy.cooling_mode = cooling_mode;
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_get_thresholds
*
****************************************************************************/
acpi_status
tz_get_thresholds (
TZ_CONTEXT *tz)
{
acpi_status status = AE_OK;
TZ_THRESHOLDS *thresholds = NULL;
u32 value = 0;
u32 i = 0;
FUNCTION_TRACE("acpi_tz_get_thresholds");
if (!tz) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
thresholds = &(tz->policy.thresholds);
/* Critical Shutdown (required) */
status = bm_evaluate_simple_integer(tz->acpi_handle, "_CRT", &value);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No critical threshold\n"));
return_ACPI_STATUS(status);
}
else {
thresholds->critical.temperature = value;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found critical threshold [%d]\n", thresholds->critical.temperature));
}
/* Critical Sleep (optional) */
status = bm_evaluate_simple_integer(tz->acpi_handle, "_HOT", &value);
if (ACPI_FAILURE(status)) {
thresholds->hot.is_valid = 0;
thresholds->hot.temperature = 0;
}
else {
thresholds->hot.is_valid = 1;
thresholds->hot.temperature = value;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%d]\n", thresholds->hot.temperature));
}
/* Passive: Processors (optional) */
status = bm_evaluate_simple_integer(tz->acpi_handle, "_PSV", &value);
if (ACPI_FAILURE(status)) {
thresholds->passive.is_valid = 0;
thresholds->passive.temperature = 0;
}
else {
thresholds->passive.is_valid = 1;
thresholds->passive.temperature = value;
status = bm_evaluate_simple_integer(tz->acpi_handle, "_TC1", &value);
if (ACPI_FAILURE(status)) {
thresholds->passive.is_valid = 0;
}
thresholds->passive.tc1 = value;
status = bm_evaluate_simple_integer(tz->acpi_handle, "_TC2", &value);
if (ACPI_FAILURE(status)) {
thresholds->passive.is_valid = 0;
}
thresholds->passive.tc2 = value;
status = bm_evaluate_simple_integer(tz->acpi_handle, "_TSP", &value);
if (ACPI_FAILURE(status)) {
thresholds->passive.is_valid = 0;
}
thresholds->passive.tsp = value;
status = bm_evaluate_reference_list(tz->acpi_handle, "_PSL", &(thresholds->passive.devices));
if (ACPI_FAILURE(status)) {
thresholds->passive.is_valid = 0;
}
if (thresholds->passive.is_valid) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found passive threshold [%d]\n", thresholds->passive.temperature));
}
else {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid passive threshold\n"));
}
}
/* Active: Fans, etc. (optional) */
for (i=0; i<TZ_MAX_ACTIVE_THRESHOLDS; i++) {
char name[5] = {'_','A','C',('0'+i),'\0'};
status = bm_evaluate_simple_integer(tz->acpi_handle, name, &value);
if (ACPI_FAILURE(status)) {
thresholds->active[i].is_valid = 0;
thresholds->active[i].temperature = 0;
break;
}
thresholds->active[i].temperature = value;
name[2] = 'L';
status = bm_evaluate_reference_list(tz->acpi_handle, name, &(thresholds->active[i].devices));
if (ACPI_SUCCESS(status)) {
thresholds->active[i].is_valid = 1;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found active threshold [%d]:[%d]\n", i, thresholds->active[i].temperature));
}
else {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid active threshold [%d]\n", i));
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_add_device
*
****************************************************************************/
acpi_status
tz_add_device (
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
TZ_CONTEXT *tz = NULL;
BM_DEVICE *device = NULL;
acpi_handle tmp_handle = NULL;
static u32 zone_count = 0;
FUNCTION_TRACE("tz_add_device");
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding thermal zone [%02x].\n", device_handle));
if (!context || *context) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Invalid context for device [%02x].\n", device_handle));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
/*
* Get information on this device.
*/
status = bm_get_device_info(device_handle, &device);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Allocate a new Thermal Zone device.
*/
tz = acpi_os_callocate(sizeof(TZ_CONTEXT));
if (!tz) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
tz->device_handle = device->handle;
tz->acpi_handle = device->acpi_handle;
/* TBD: How to manage 'uid' when zones are Pn_p? */
sprintf(tz->uid, "%d", zone_count++);
/*
* Temperature:
* ------------
* Make sure we can read the zone's current temperature (_TMP).
* If we can't, there's no use in doing any policy (abort).
*/
status = tz_get_temperature(tz);
if (ACPI_FAILURE(status))
goto end;
/*
* Polling Frequency:
* ------------------
* If _TZP doesn't exist use the OS default polling frequency.
*/
status = bm_evaluate_simple_integer(tz->acpi_handle, "_TZP", &(tz->policy.polling_freq));
if (ACPI_FAILURE(status)) {
tz->policy.polling_freq = TZP;
}
status = AE_OK;
/*
* Cooling Preference:
* -------------------
* Default to ACTIVE (noisy) cooling until policy decides otherwise.
* Note that _SCP is optional.
*/
tz_set_cooling_preference(tz, TZ_COOLING_MODE_ACTIVE);
/*
* Start Policy:
* -------------
* Thermal policy is included in the kernel (this driver) because
* of the critical role it plays in avoiding nuclear meltdown. =O
*/
status = tz_policy_add_device(tz);
if (ACPI_FAILURE(status))
goto end;
status = tz_osl_add_device(tz);
if (ACPI_FAILURE(status))
goto end;
*context = tz;
tz_print(tz);
end:
if (ACPI_FAILURE(status))
acpi_os_free(tz);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_remove_device
*
****************************************************************************/
acpi_status
tz_remove_device (
void **context)
{
acpi_status status = AE_OK;
TZ_CONTEXT *tz = NULL;
FUNCTION_TRACE("tz_remove_device");
if (!context || !*context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
tz = (TZ_CONTEXT*)(*context);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing thermal zone [%02x].\n", tz->device_handle));
status = tz_osl_remove_device(tz);
/*
* Remove Policy:
* --------------
* TBD: Move all thermal zone policy to user-mode daemon...
*/
status = tz_policy_remove_device(tz);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
acpi_os_free(tz);
return_ACPI_STATUS(status);
}
/****************************************************************************
* External Functions
****************************************************************************/
/****************************************************************************
*
* FUNCTION: tz_initialize
*
****************************************************************************/
acpi_status
tz_initialize (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("tz_initialize");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Register driver for thermal zone devices.
*/
criteria.type = BM_TYPE_THERMAL_ZONE;
driver.notify = &tz_notify;
driver.request = &tz_request;
status = bm_register_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_terminate
*
****************************************************************************/
acpi_status
tz_terminate (void)
{
acpi_status status = AE_OK;
BM_DEVICE_ID criteria;
BM_DRIVER driver;
FUNCTION_TRACE("tz_terminate");
memset(&criteria, 0, sizeof(BM_DEVICE_ID));
memset(&driver, 0, sizeof(BM_DRIVER));
/*
* Unregister driver for thermal zone devices.
*/
criteria.type = BM_TYPE_THERMAL_ZONE;
driver.notify = &tz_notify;
driver.request = &tz_request;
status = bm_unregister_driver(&criteria, &driver);
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_notify
*
****************************************************************************/
acpi_status
tz_notify (
BM_NOTIFY notify_type,
BM_HANDLE device_handle,
void **context)
{
acpi_status status = AE_OK;
TZ_CONTEXT *tz = NULL;
FUNCTION_TRACE("tz_notify");
if (!context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
tz = (TZ_CONTEXT*)*context;
switch (notify_type) {
case BM_NOTIFY_DEVICE_ADDED:
status = tz_add_device(device_handle, context);
break;
case BM_NOTIFY_DEVICE_REMOVED:
status = tz_remove_device(context);
break;
case TZ_NOTIFY_TEMPERATURE_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Temperature (_TMP) change event detected.\n"));
tz_policy_check(*context);
status = tz_get_temperature(tz);
if (ACPI_SUCCESS(status)) {
status = tz_osl_generate_event(notify_type, tz);
}
break;
case TZ_NOTIFY_THRESHOLD_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Threshold (_SCP) change event detected.\n"));
status = tz_policy_remove_device(tz);
if (ACPI_SUCCESS(status)) {
status = tz_policy_add_device(tz);
}
status = tz_osl_generate_event(notify_type, tz);
break;
case TZ_NOTIFY_DEVICE_LISTS_CHANGE:
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device lists (_ALx, _PSL, _TZD) change event detected.\n"));
status = tz_policy_remove_device(tz);
if (ACPI_SUCCESS(status)) {
status = tz_policy_add_device(tz);
}
status = tz_osl_generate_event(notify_type, tz);
break;
default:
status = AE_SUPPORT;
break;
}
return_ACPI_STATUS(status);
}
/****************************************************************************
*
* FUNCTION: tz_request
*
****************************************************************************/
acpi_status
tz_request (
BM_REQUEST *request,
void *context)
{
acpi_status status = AE_OK;
TZ_CONTEXT *tz = NULL;
FUNCTION_TRACE("tz_request");
/*
* Must have a valid request structure and context.
*/
if (!request || !context) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
tz = (TZ_CONTEXT*)context;
/*
* Handle request:
* ---------------
*/
switch (request->command) {
default:
status = AE_SUPPORT;
break;
}
request->status = status;
return_ACPI_STATUS(status);
}
/******************************************************************************
*
* Module Name: tz_osl.c
* $Revision: 25 $
*
*****************************************************************************/
/*
* 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 <acpi.h>
#include "tz.h"
MODULE_AUTHOR("Andrew Grover");
MODULE_DESCRIPTION("ACPI Component Architecture (CA) - Thermal Zone Driver");
MODULE_LICENSE("GPL");
int TZP = 0;
MODULE_PARM(TZP, "i");
MODULE_PARM_DESC(TZP, "Thermal zone polling frequency, in 1/10 seconds.\n");
#define TZ_PROC_ROOT "thermal"
#define TZ_PROC_STATUS "status"
#define TZ_PROC_INFO "info"
extern struct proc_dir_entry *bm_proc_root;
static struct proc_dir_entry *tz_proc_root = NULL;
/****************************************************************************
*
* FUNCTION: tz_osl_proc_read_info
*
****************************************************************************/
static int
tz_osl_proc_read_info (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
acpi_status status = AE_OK;
char name[5];
acpi_buffer buffer = {sizeof(name), &name};
TZ_CONTEXT *tz = NULL;
TZ_THRESHOLDS *thresholds = NULL;
char *p = page;
int len = 0;
u32 i,j;
u32 t = 0;
if (!context || (off != 0))
goto end;
tz = (TZ_CONTEXT*)context;
thresholds = &(tz->policy.thresholds);
p += sprintf(p, "critical (S5): trip=%d\n", thresholds->critical.temperature);
if (thresholds->hot.is_valid)
p += sprintf(p, "critical (S4): trip=%d\n", thresholds->hot.temperature);
if (thresholds->passive.is_valid) {
p += sprintf(p, "passive: trip=%d tc1=%d tc2=%d tsp=%d devices=", thresholds->passive.temperature, thresholds->passive.tc1, thresholds->passive.tc2, thresholds->passive.tsp);
for (j=0; j<thresholds->passive.devices.count; j++)
p += sprintf(p, "%08x%c", thresholds->passive.devices.handles[j], (j==thresholds->passive.devices.count-1)?'\n':',');
}
for (i=0; i<TZ_MAX_ACTIVE_THRESHOLDS; i++) {
if (!(thresholds->active[i].is_valid))
break;
p += sprintf(p, "active[%d]: trip=%d devices=", i, thresholds->active[i].temperature);
for (j=0; j<thresholds->active[i].devices.count; j++)
p += sprintf(p, "%08x%c", thresholds->active[i].devices.handles[j], (j==thresholds->passive.devices.count-1)?'\n':',');
}
p += sprintf(p, "cooling mode: ");
switch (tz->policy.cooling_mode) {
case TZ_COOLING_MODE_ACTIVE:
p += sprintf(p, "active (noisy)\n");
break;
case TZ_COOLING_MODE_PASSIVE:
p += sprintf(p, "passive (quiet)\n");
break;
default:
p += sprintf(p, "unknown\n");
break;
}
p += sprintf(p, "polling: ");
switch (tz->policy.polling_freq) {
case 0:
p += sprintf(p, "disabled\n");
break;
default:
p += sprintf(p, "%d dS\n", tz->policy.polling_freq);
break;
}
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: tz_osl_proc_write_info
*
****************************************************************************/
static int tz_osl_proc_write_info (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
TZ_CONTEXT *tz = NULL;
u32 state = 0;
u32 size = 0;
if (!buffer || (count==0) || !data) {
goto end;
}
tz = (TZ_CONTEXT*)data;
size = strlen(buffer);
if (size < 4)
goto end;
/* Cooling preference: "scp=0" (active) or "scp=1" (passive) */
if (0 == strncmp(buffer, "scp=", 4)) {
tz_set_cooling_preference(tz, (buffer[4] - '0'));
}
/* Polling frequency: "tzp=X" (poll every X [0-9] seconds) */
else if (0 == strncmp(buffer, "tzp=", 4)) {
tz->policy.polling_freq = (buffer[4] - '0') * 10;
tz_policy_check(tz);
}
end:
return count;
}
/****************************************************************************
*
* FUNCTION: tz_osl_proc_read_status
*
****************************************************************************/
static int
tz_osl_proc_read_status (
char *page,
char **start,
off_t off,
int count,
int *eof,
void *context)
{
TZ_CONTEXT *tz = NULL;
char *p = page;
int len = 0;
if (!context || (off != 0)) {
goto end;
}
tz = (TZ_CONTEXT*)context;
/* Temperature */
tz_get_temperature(tz);
p += sprintf(p, "temperature: %d dK\n", tz->policy.temperature);
p += sprintf(p, "state: ");
if (tz->policy.state == 0)
p += sprintf(p, "ok\n");
else if (tz->policy.state & TZ_STATE_CRITICAL)
p += sprintf(p, "critical\n");
else if (tz->policy.state & TZ_STATE_HOT)
p += sprintf(p, "hot\n");
else {
if (tz->policy.state & TZ_STATE_ACTIVE)
p += sprintf(p, "active[%d] ", tz->policy.state & 0x07);
if (tz->policy.state & TZ_STATE_PASSIVE)
p += sprintf(p, "passive ");
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: tz_osl_add_device
*
****************************************************************************/
acpi_status
tz_osl_add_device(
TZ_CONTEXT *tz)
{
struct proc_dir_entry *proc_entry = NULL;
struct proc_dir_entry *proc_child_entry = NULL;
if (!tz) {
return(AE_BAD_PARAMETER);
}
printk("ACPI: Thermal Zone found\n");
proc_entry = proc_mkdir(tz->uid, tz_proc_root);
if (!proc_entry)
return(AE_ERROR);
proc_child_entry = create_proc_read_entry(TZ_PROC_STATUS, S_IFREG | S_IRUGO, proc_entry, tz_osl_proc_read_status, (void*)tz);
if (!proc_child_entry)
return(AE_ERROR);
proc_child_entry = create_proc_entry(TZ_PROC_INFO, S_IFREG | 0644, proc_entry);
if (!proc_child_entry)
return(AE_ERROR);
proc_child_entry->read_proc = tz_osl_proc_read_info;
proc_child_entry->write_proc = tz_osl_proc_write_info;
proc_child_entry->data = (void*)tz;
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_osl_remove_device
*
****************************************************************************/
acpi_status
tz_osl_remove_device (
TZ_CONTEXT *tz)
{
char proc_entry[64];
if (!tz) {
return(AE_BAD_PARAMETER);
}
sprintf(proc_entry, "%s/%s", tz->uid, TZ_PROC_INFO);
remove_proc_entry(proc_entry, tz_proc_root);
sprintf(proc_entry, "%s/%s", tz->uid, TZ_PROC_STATUS);
remove_proc_entry(proc_entry, tz_proc_root);
sprintf(proc_entry, "%s", tz->uid);
remove_proc_entry(proc_entry, tz_proc_root);
return(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_osl_generate_event
*
****************************************************************************/
acpi_status
tz_osl_generate_event (
u32 event,
TZ_CONTEXT *tz)
{
acpi_status status = AE_OK;
if (!tz) {
return(AE_BAD_PARAMETER);
}
switch (event) {
case TZ_NOTIFY_TEMPERATURE_CHANGE:
status = bm_osl_generate_event(tz->device_handle,
TZ_PROC_ROOT, tz->uid, event,
tz->policy.temperature);
break;
case TZ_NOTIFY_THRESHOLD_CHANGE:
case TZ_NOTIFY_DEVICE_LISTS_CHANGE:
status = bm_osl_generate_event(tz->device_handle,
TZ_PROC_ROOT, tz->uid, event, 0);
break;
default:
return(AE_BAD_PARAMETER);
break;
}
return(status);
}
/****************************************************************************
*
* FUNCTION: tz_osl_init
*
****************************************************************************/
static int __init
tz_osl_init (void)
{
acpi_status status = AE_OK;
/* abort if no busmgr */
if (!bm_proc_root)
return -ENODEV;
tz_proc_root = proc_mkdir(TZ_PROC_ROOT, bm_proc_root);
if (!tz_proc_root) {
status = AE_ERROR;
}
else {
status = tz_initialize();
if (ACPI_FAILURE(status)) {
remove_proc_entry(TZ_PROC_ROOT, bm_proc_root);
}
}
return (ACPI_SUCCESS(status)) ? 0 : -ENODEV;
}
/****************************************************************************
*
* FUNCTION: tz_osl_cleanup
*
****************************************************************************/
static void __exit
tz_osl_cleanup (void)
{
tz_terminate();
if (tz_proc_root) {
remove_proc_entry(TZ_PROC_ROOT, bm_proc_root);
}
return;
}
module_init(tz_osl_init);
module_exit(tz_osl_cleanup);
/****************************************************************************
*
* Module Name: tzpolicy.c -
* $Revision: 30 $
*
****************************************************************************/
/*
* 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
*/
/*
* TBD: 1. Support performance-limit control for non-processor devices
* (those listed in _TZD, e.g. graphics).
*/
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/pm.h>
#include <linux/sched.h>
#include <acpi.h>
#include <bm.h>
#include "tz.h"
#define _COMPONENT ACPI_THERMAL
MODULE_NAME ("tzpolicy")
/****************************************************************************
* Globals
****************************************************************************/
void
tz_policy_run (
unsigned long data);
/****************************************************************************
* Internal Functions
****************************************************************************/
acpi_status
set_performance_limit (
BM_HANDLE device_handle,
u32 flag)
{
acpi_status status;
BM_REQUEST request;
request.status = AE_OK;
request.handle = device_handle;
request.command = PR_COMMAND_SET_PERF_LIMIT;
request.buffer.length = sizeof(u32);
request.buffer.pointer = &flag;
status = bm_request(&request);
if (ACPI_FAILURE(status))
return status;
else
return request.status;
}
/****************************************************************************
*
* FUNCTION: tz_policy_critical
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_critical(
TZ_CONTEXT *tz)
{
FUNCTION_TRACE("tz_policy_critical");
if (!tz) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (tz->policy.temperature >= tz->policy.thresholds.critical.temperature) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Critical (S5) threshold reached.\n"));
/* TBD: Need method for shutting down system. */
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_policy_hot
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_hot(
TZ_CONTEXT *tz)
{
FUNCTION_TRACE("tz_policy_hot");
if (!tz || !tz->policy.thresholds.hot.is_valid) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (tz->policy.temperature >= tz->policy.thresholds.hot.temperature) {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Critical (S4) threshold reached.\n"));
/* TBD: Need method for invoking OS-level critical suspend. */
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_policy_passive
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_passive(
TZ_CONTEXT *tz)
{
TZ_PASSIVE_THRESHOLD *passive = NULL;
static u32 last_temperature = 0;
s32 trend = 0;
u32 i = 0;
FUNCTION_TRACE("tz_policy_passive");
if (!tz || !tz->policy.thresholds.passive.is_valid) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
passive = &(tz->policy.thresholds.passive);
if (tz->policy.temperature >= passive->temperature) {
/*
* Thermal trend?
* --------------
* Using the passive cooling equation (see the ACPI
* Specification), calculate the current thermal trend
* (a.k.a. performance delta).
*/
trend = passive->tc1 * (tz->policy.temperature - last_temperature) + passive->tc2 * (tz->policy.temperature - passive->temperature);
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "trend[%d] = TC1[%d]*(temp[%d]-last[%d]) + TC2[%d]*(temp[%d]-passive[%d])\n", trend, passive->tc1, tz->policy.temperature, last_temperature, passive->tc2, tz->policy.temperature, passive->temperature));
last_temperature = tz->policy.temperature;
/*
* Heating Up?
* -----------
* Decrease thermal performance limit on all passive
* cooling devices (processors).
*/
if (trend > 0) {
for (i=0; i<passive->devices.count; i++)
set_performance_limit(passive->devices.handles[i], PR_PERF_DEC);
}
/*
* Cooling Off?
* ------------
* Increase thermal performance limit on all passive
* cooling devices (processors).
*/
else if (trend < 0) {
for (i=0; i<passive->devices.count; i++)
set_performance_limit(passive->devices.handles[i], PR_PERF_INC);
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_policy_active
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_active(
TZ_CONTEXT *tz)
{
acpi_status status = AE_OK;
TZ_ACTIVE_THRESHOLD *active = NULL;
u32 i,j = 0;
FUNCTION_TRACE("tz_policy_active");
if (!tz || !tz->policy.thresholds.active[0].is_valid) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
for (i=0; i<TZ_MAX_ACTIVE_THRESHOLDS; i++) {
active = &(tz->policy.thresholds.active[i]);
if (!active || !active->is_valid)
break;
/*
* Above Threshold?
* ----------------
* If not already enabled, turn ON all cooling devices
* associated with this active threshold.
*/
if ((tz->policy.temperature >= active->temperature) && (active->cooling_state != TZ_COOLING_ENABLED)) {
for (j = 0; j < active->devices.count; j++) {
status = bm_set_device_power_state(active->devices.handles[j], ACPI_STATE_D0);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Cooling device [%02x] now ON.\n", active->devices.handles[j]));
}
else {
ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Unable to turn ON cooling device [%02x].\n", active->devices.handles[j]));
}
}
active->cooling_state = TZ_COOLING_ENABLED;
}
/*
* Below Threshold?
* ----------------
* Turn OFF all cooling devices associated with this
* threshold. Note that by checking "if not disabled" we
* turn off all cooling devices for thresholds in the
* TZ_COOLING_STATE_UNKNOWN state, useful as a level-set
* during the first pass.
*/
else if (active->cooling_state != TZ_COOLING_DISABLED) {
for (j = 0; j < active->devices.count; j++) {
status = bm_set_device_power_state(active->devices.handles[j], ACPI_STATE_D3);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Cooling device [%02x] now OFF.\n", active->devices.handles[j]));
}
else {
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Unable to turn OFF cooling device [%02x].\n", active->devices.handles[j]));
}
}
active->cooling_state = TZ_COOLING_DISABLED;
}
}
return_ACPI_STATUS(AE_OK);
}
/****************************************************************************
*
* FUNCTION: tz_policy_check
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION: Note that this function will get called whenever:
* 1. A thermal event occurs.
* 2. The polling/sampling time period expires.
*
****************************************************************************/
void
tz_policy_check (
void *context)
{
acpi_status status = AE_OK;
TZ_CONTEXT *tz = NULL;
TZ_POLICY *policy = NULL;
TZ_THRESHOLDS *thresholds = NULL;
u32 previous_temperature = 0;
u32 previous_state = 0;
u32 active_index = 0;
u32 i = 0;
u32 sleep_time = 0;
FUNCTION_TRACE("tz_policy_check");
if (!context) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return_VOID;
}
tz = (TZ_CONTEXT*)context;
policy = &(tz->policy);
thresholds = &(tz->policy.thresholds);
/*
* Preserve Previous State:
* ------------------------
*/
previous_temperature = tz->policy.temperature;
previous_state = tz->policy.state;
/*
* Get Temperature:
* ----------------
*/
status = tz_get_temperature(tz);
if (ACPI_FAILURE(status)) {
return_VOID;
}
/*
* Calculate State:
* ----------------
*/
policy->state = TZ_STATE_OK;
/* Critical? */
if (policy->temperature >= thresholds->critical.temperature)
policy->state |= TZ_STATE_CRITICAL;
/* Hot? */
if ((thresholds->hot.is_valid) && (policy->temperature >= thresholds->hot.temperature))
policy->state |= TZ_STATE_CRITICAL;
/* Passive? */
if ((thresholds->passive.is_valid) && (policy->temperature >= thresholds->passive.temperature))
policy->state |= TZ_STATE_PASSIVE;
/* Active? */
if (thresholds->active[0].is_valid) {
for (i=0; i<TZ_MAX_ACTIVE_THRESHOLDS; i++) {
if ((thresholds->active[i].is_valid) && (policy->temperature >= thresholds->active[i].temperature)) {
policy->state |= TZ_STATE_ACTIVE;
if (i > active_index)
active_index = i;
}
}
policy->state |= active_index;
}
/*
* Invoke Policy:
* --------------
* Note that policy must be invoked both when 'going into' a
* policy state (e.g. to allow fans to be turned on) and 'going
* out of' a policy state (e.g. to allow fans to be turned off);
* thus we must preserve the previous state.
*/
if (policy->state & TZ_STATE_CRITICAL)
tz_policy_critical(tz);
if (policy->state & TZ_STATE_HOT)
tz_policy_hot(tz);
if ((policy->state & TZ_STATE_PASSIVE) || (previous_state & TZ_STATE_PASSIVE))
tz_policy_passive(tz);
if ((policy->state & TZ_STATE_ACTIVE) || (previous_state & TZ_STATE_ACTIVE))
tz_policy_active(tz);
/*
* Calculate Sleep Time:
* ---------------------
* If we're in the passive state, use _TSP's value. Otherwise
* use _TZP or the OS's default polling frequency. If no polling
* frequency is specified then we'll wait forever (that is, until
* a thermal event occurs -- e.g. never poll). Note that _TSP
* and _TZD values are given in 1/10th seconds.
*/
if (policy->state & TZ_STATE_PASSIVE)
sleep_time = thresholds->passive.tsp * 100;
else if (policy->polling_freq > 0)
sleep_time = policy->polling_freq * 100;
else
sleep_time = WAIT_FOREVER;
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Thermal_zone[%02x]: temperature[%d] state[%08x]\n", tz->device_handle, policy->temperature, policy->state));
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Scheduling next poll in [%d]ms.\n", sleep_time));
/*
* Schedule Next Poll:
* -------------------
*/
if (sleep_time < WAIT_FOREVER) {
if (timer_pending(&(policy->timer)))
mod_timer(&(policy->timer), (HZ*sleep_time)/1000);
else {
policy->timer.data = (u32)tz;
policy->timer.function = tz_policy_run;
policy->timer.expires = jiffies + (HZ*sleep_time)/1000;
add_timer(&(policy->timer));
}
}
else {
if (timer_pending(&(policy->timer)))
del_timer(&(policy->timer));
}
return_VOID;
}
/****************************************************************************
*
* FUNCTION: tz_policy_run
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
void
tz_policy_run (
unsigned long data)
{
acpi_status status = AE_OK;
FUNCTION_TRACE("tz_policy_run");
if (!data) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
return_VOID;
}
/*
* Defer to Non-Interrupt Level:
* -----------------------------
* Note that all Linux kernel timers run at interrupt-level (ack!).
*/
status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, tz_policy_check, (void*)data);
if (ACPI_FAILURE(status)) {
ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Error invoking thermal policy.\n"));
}
return_VOID;
}
/****************************************************************************
*
* FUNCTION: tz_policy_add_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_add_device (
TZ_CONTEXT *tz)
{
acpi_status status = AE_OK;
TZ_THRESHOLDS *thresholds = NULL;
u32 i,j = 0;
FUNCTION_TRACE("tz_policy_add_device");
if (!tz) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Adding policy for thermal zone [%02x].\n", tz->device_handle));
/*
* Get Thresholds:
* ---------------
*/
status = tz_get_thresholds(tz);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/*
* Initialize Policies:
* --------------------
*/
if (tz->policy.thresholds.passive.is_valid) {
for (i=0; i<tz->policy.thresholds.passive.devices.count; i++)
set_performance_limit(tz->policy.thresholds.passive.devices.handles[i], PR_PERF_MAX);
tz_policy_passive(tz);
}
if (tz->policy.thresholds.active[0].is_valid)
tz_policy_active(tz);
/*
* Initialize Policy Timer:
* ------------------------
*/
init_timer(&(tz->policy.timer));
/*
* Start Policy:
* -------------
* Run an initial check using this zone's policy.
*/
tz_policy_check(tz);
return_ACPI_STATUS(AE_OK);
}
/*****************************************************************************
*
* FUNCTION: tz_policy_remove_device
*
* PARAMETERS:
*
* RETURN:
*
* DESCRIPTION:
*
****************************************************************************/
acpi_status
tz_policy_remove_device(
TZ_CONTEXT *tz)
{
u32 i = 0;
FUNCTION_TRACE("tz_remove_device");
if (!tz) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing policy for thermal zone [%02x].\n", tz->device_handle));
/*
* Delete the thermal zone policy timer entry, if exists.
*/
if (timer_pending(&(tz->policy.timer)))
del_timer(&(tz->policy.timer));
/*
* Reset thermal performance limit on all processors back to max.
*/
if (tz->policy.thresholds.passive.is_valid) {
for (i=0; i<tz->policy.thresholds.passive.devices.count; i++)
set_performance_limit(tz->policy.thresholds.passive.devices.handles[i], PR_PERF_MAX);
}
return_ACPI_STATUS(AE_OK);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment