diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-08 00:53:00 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-08 00:53:00 +0000 |
commit | b8553086288629b4efb77e97f5582e08bc50ad65 (patch) | |
tree | 0a19bd1c21e148f35c7a0f76baa4f7a056b966b0 /drivers | |
parent | 75b6d92f2dd5112b02f4e78cf9f35f9825946ef0 (diff) |
Merge with 2.4.0-test3-pre4.
Diffstat (limited to 'drivers')
308 files changed, 69757 insertions, 2235 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 9d42ab23d..a91c3355e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,7 +11,8 @@ SUB_DIRS := block char net parport sound misc MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi ide scsi sbus cdrom isdn pnp i2o \ ieee1394 macintosh video dio zorro fc4 \ - usb nubus tc atm pcmcia i2c telephony + usb nubus tc atm pcmcia i2c telephony \ + acpi mtd ifdef CONFIG_DIO SUB_DIRS += dio @@ -30,6 +31,11 @@ else endif endif +ifdef CONFIG_MTD +SUB_DIRS += mtd +MOD_SUB_DIRS += mtd +endif + ifdef CONFIG_SBUS SUB_DIRS += sbus MOD_SUB_DIRS += sbus @@ -180,4 +186,9 @@ else endif endif +ifeq ($(CONFIG_ACPI),y) +SUB_DIRS += acpi +MOD_SUB_DIRS += acpi +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c index e2e7d96df..980add976 100644 --- a/drivers/acorn/block/fd1772.c +++ b/drivers/acorn/block/fd1772.c @@ -289,6 +289,7 @@ static unsigned int changed_floppies = 0xff, fake_change = 0; #define MAX_ERRORS 8 /* After this many errors the driver * will give up. */ +static struct timer_list fd_timer; #define START_MOTOR_OFF_TIMER(delay) \ do { \ @@ -299,8 +300,7 @@ static unsigned int changed_floppies = 0xff, fake_change = 0; #define START_CHECK_CHANGE_TIMER(delay) \ do { \ - timer_table[FLOPPY_TIMER].expires = jiffies + (delay); \ - timer_active |= (1 << FLOPPY_TIMER); \ + mod_timer(&fd_timer, jiffies + (delay)); \ } while(0) #define START_TIMEOUT() \ @@ -340,7 +340,7 @@ static void fd_select_side(int side); static void fd_select_drive(int drive); static void fd_deselect(void); static void fd_motor_off_timer(unsigned long dummy); -static void check_change(void); +static void check_change(unsigned long dummy); static __inline__ void set_head_settle_flag(void); static __inline__ int get_head_settle_flag(void); static void floppy_irqconsequencehandler(void); @@ -501,7 +501,7 @@ static void fd_motor_off_timer(unsigned long dummy) * as possible) and keep track of the current state of the write protection. */ -static void check_change(void) +static void check_change(unsigned long dummy) { static int drive = 0; @@ -1093,12 +1093,12 @@ static void finish_fdc_done(int dummy) STOP_TIMEOUT(); NeedSeek = 0; - if ((timer_active & (1 << FLOPPY_TIMER)) && - time_after(jiffies + 5, timer_table[FLOPPY_TIMER].expires)) + if (timer_pending(&fd_timer) && + time_after(jiffies + 5, fd_timer.expires)) /* If the check for a disk change is done too early after this * last seek command, the WP bit still reads wrong :-(( */ - timer_table[FLOPPY_TIMER].expires = jiffies + 5; + mod_timer(&fd_timer, jiffies + 5); else { /* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */ }; @@ -1602,9 +1602,9 @@ int fd1772_init(void) #endif /* initialize check_change timer */ - timer_table[FLOPPY_TIMER].fn = check_change; - timer_active &= ~(1 << FLOPPY_TIMER); - + init_timer(&fd_timer); + fd_timer.function = check_change; +} #ifdef TRACKBUFFER DMABuffer = (char *)kmalloc((MAX_SECTORS+1)*512,GFP_KERNEL); /* Atari uses 512 - I want to eventually cope with 1K sectors */ diff --git a/drivers/acpi/.cvsignore b/drivers/acpi/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/acpi/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile new file mode 100644 index 000000000..c8b888ff1 --- /dev/null +++ b/drivers/acpi/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the Linux ACPI interpreter +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := +ALL_SUB_DIRS := $(SUB_DIRS) + +O_TARGET := acpi.o +O_OBJS := +M_OBJS := + +ACPI_OBJS := osd.o +ACPI_OBJS += $(patsubst %.c,%.o,$(wildcard */*.c)) + +EXTRA_CFLAGS += -I./include -D_LINUX + +# if the interpreter is used, it overrides arch/i386/kernel/acpi.c +ifeq ($(CONFIG_ACPI_INTERPRETER),y) + O_OBJS += $(ACPI_OBJS) +endif + +include $(TOPDIR)/Rules.make + +clean: + $(RM) *.o */*.o diff --git a/drivers/acpi/common/cmalloc.c b/drivers/acpi/common/cmalloc.c new file mode 100644 index 000000000..3cea827da --- /dev/null +++ b/drivers/acpi/common/cmalloc.c @@ -0,0 +1,166 @@ +/****************************************************************************** + * + * Module Name: cmalloc - local memory allocation routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "interp.h" +#include "namesp.h" +#include "globals.h" + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmalloc"); + + +/***************************************************************************** + * + * FUNCTION: _Cm_allocate + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: The subsystem's equivalent of malloc. + * + ****************************************************************************/ + +void * +_cm_allocate ( + u32 size, + u32 component, + ACPI_STRING module, + s32 line) +{ + void *address = NULL; + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + REPORT_ERROR ("Cm_allocate: Attempt to allocate zero bytes"); + size = 1; + } + + address = acpi_os_allocate (size); + if (!address) { + /* Report allocation error */ + + _REPORT_ERROR (module, line, component, + "Cm_allocate: Memory allocation failure"); + + return (NULL); + } + + + return (address); +} + + +/***************************************************************************** + * + * FUNCTION: _Cm_callocate + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: Subsystem equivalent of calloc. + * + ****************************************************************************/ + +void * +_cm_callocate ( + u32 size, + u32 component, + ACPI_STRING module, + s32 line) +{ + void *address = NULL; + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + REPORT_ERROR ("Cm_callocate: Attempt to allocate zero bytes"); + return (NULL); + } + + + address = acpi_os_callocate (size); + + if (!address) { + /* Report allocation error */ + + _REPORT_ERROR (module, line, component, + "Cm_callocate: Memory allocation failure"); + + return (NULL); + } + + + return (address); +} + + +/***************************************************************************** + * + * FUNCTION: _Cm_free + * + * PARAMETERS: Address - Address of the memory to deallocate + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: None + * + * DESCRIPTION: Frees the memory at Address + * + ****************************************************************************/ + +void +_cm_free ( + void *address, + u32 component, + ACPI_STRING module, + s32 line) +{ + + if (NULL == address) { + _REPORT_ERROR (module, line, component, + "_Cm_free: Trying to delete a NULL address."); + + return; + } + + + acpi_os_free (address); + + return; +} + + diff --git a/drivers/acpi/common/cmcopy.c b/drivers/acpi/common/cmcopy.c new file mode 100644 index 000000000..c5a828c62 --- /dev/null +++ b/drivers/acpi/common/cmcopy.c @@ -0,0 +1,711 @@ +/****************************************************************************** + * + * Module Name: cmcopy - Internal to external object translation utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmcopy"); + + +typedef struct search_st +{ + ACPI_OBJECT_INTERNAL *internal_obj; + u32 index; + ACPI_OBJECT *external_obj; + +} PKG_SEARCH_INFO; + + +/* Used to traverse nested packages */ + +PKG_SEARCH_INFO level[MAX_PACKAGE_DEPTH]; + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_external_simple_object + * + * PARAMETERS: *Internal_obj - Pointer to the object we are examining + * *Buffer - Where the object is returned + * *Space_used - Where the data length is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to place a simple object in a user + * buffer. + * + * The buffer is assumed to have sufficient space for the object. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_external_simple_object ( + ACPI_OBJECT_INTERNAL *internal_obj, + ACPI_OBJECT *external_obj, + u8 *data_space, + u32 *buffer_space_used) +{ + u32 length = 0; + char *source_ptr = NULL; + + + /* + * Check for NULL object case (could be an uninitialized + * package element + */ + + if (!internal_obj) { + *buffer_space_used = 0; + return (AE_OK); + } + + /* Always clear the external object */ + + MEMSET (external_obj, 0, sizeof (ACPI_OBJECT)); + + /* + * In general, the external object will be the same type as + * the internal object + */ + + external_obj->type = internal_obj->common.type; + + /* However, only a limited number of external types are supported */ + + switch (external_obj->type) + { + + case ACPI_TYPE_STRING: + + length = internal_obj->string.length; + external_obj->string.length = internal_obj->string.length; + external_obj->string.pointer = (char *) data_space; + source_ptr = internal_obj->string.pointer; + break; + + + case ACPI_TYPE_BUFFER: + + length = internal_obj->buffer.length; + external_obj->buffer.length = internal_obj->buffer.length; + external_obj->buffer.pointer = data_space; + source_ptr = (char *) internal_obj->buffer.pointer; + break; + + + case ACPI_TYPE_NUMBER: + + external_obj->number.value= internal_obj->number.value; + break; + + + case INTERNAL_TYPE_REFERENCE: + + /* + * This is an object reference. We use the object type of "Any" + * to indicate a reference object containing a handle to an ACPI + * named object. + */ + + external_obj->type = ACPI_TYPE_ANY; + external_obj->reference.handle = internal_obj->reference.nte; + break; + + + case ACPI_TYPE_PROCESSOR: + + external_obj->processor.proc_id = + internal_obj->processor.proc_id; + + external_obj->processor.pblk_address = + internal_obj->processor.pblk_address; + + external_obj->processor.pblk_length = + internal_obj->processor.pblk_length; + break; + + case ACPI_TYPE_POWER: + + external_obj->power_resource.system_level = + internal_obj->power_resource.system_level; + + external_obj->power_resource.resource_order = + internal_obj->power_resource.resource_order; + break; + + default: + return (AE_CTRL_RETURN_VALUE); + break; + } + + + /* Copy data if necessary (strings or buffers) */ + + if (length) { + /* + * Copy the return data to the caller's buffer + */ + MEMCPY ((void *) data_space, (void *) source_ptr, length); + } + + + *buffer_space_used = (u32) ROUND_UP_TO_NATIVE_WORD (length); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_external_package_object + * + * PARAMETERS: *Internal_obj - Pointer to the object we are returning + * *Buffer - Where the object is returned + * *Space_used - Where the object length is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to place a package object in a user + * buffer. A package object by definition contains other objects. + * + * The buffer is assumed to have sufficient space for the object. + * The caller must have verified the buffer length needed using the + * Acpi_cm_get_object_size function before calling this function. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_external_package_object ( + ACPI_OBJECT_INTERNAL *internal_obj, + u8 *buffer, + u32 *space_used) +{ + u8 *free_space; + ACPI_OBJECT *external_obj; + u32 current_depth = 0; + ACPI_STATUS status; + u32 length = 0; + u32 this_index; + u32 object_space; + ACPI_OBJECT_INTERNAL *this_internal_obj; + ACPI_OBJECT *this_external_obj; + PKG_SEARCH_INFO *level_ptr; + + + /* + * First package at head of the buffer + */ + external_obj = (ACPI_OBJECT *) buffer; + + /* + * Free space begins right after the first package + */ + free_space = buffer + ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT)); + + + /* + * Initialize the working variables + */ + + MEMSET ((void *) level, 0, sizeof (level)); + + level[0].internal_obj = internal_obj; + level[0].external_obj = external_obj; + level[0].index = 0; + level_ptr = &level[0]; + current_depth = 0; + + external_obj->type = internal_obj->common.type; + external_obj->package.count = internal_obj->package.count; + external_obj->package.elements = (ACPI_OBJECT *) free_space; + + + /* + * Build an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + + free_space += external_obj->package.count * + ROUND_UP_TO_NATIVE_WORD (sizeof (ACPI_OBJECT)); + + + while (1) { + this_index = level_ptr->index; + this_internal_obj = + (ACPI_OBJECT_INTERNAL *) + level_ptr->internal_obj->package.elements[this_index]; + this_external_obj = + (ACPI_OBJECT *) + &level_ptr->external_obj->package.elements[this_index]; + + + /* + * Check for 1) Null object -- OK, this can happen if package + * element is never initialized + * 2) Not an internal object - can be an NTE instead + * 3) Any internal object other than a package. + * + * The more complex package case is handled later + */ + + if ((!this_internal_obj) || + (!VALID_DESCRIPTOR_TYPE ( + this_internal_obj, ACPI_DESC_TYPE_INTERNAL)) || + (!IS_THIS_OBJECT_TYPE ( + this_internal_obj, ACPI_TYPE_PACKAGE))) + { + /* + * This is a simple or null object -- get the size + */ + + status = + acpi_cm_build_external_simple_object (this_internal_obj, + this_external_obj, + free_space, + &object_space); + if (ACPI_FAILURE (status)) { + return (status); + } + + free_space += object_space; + length += object_space; + + level_ptr->index++; + while (level_ptr->index >= + level_ptr->internal_obj->package.count) + { + /* + * We've handled all of the objects at this + * level. This means that we have just + * completed a package. That package may + * have contained one or more packages + * itself + */ + if (current_depth == 0) { + /* + * We have handled all of the objects + * in the top level package just add + * the length of the package objects + * and get out + */ + *space_used = length; + return (AE_OK); + } + + /* + * go back up a level and move the index + * past the just completed package object. + */ + current_depth--; + level_ptr = &level[current_depth]; + level_ptr->index++; + } + } + + + else { + /* + * This object is a package + * -- we must go one level deeper + */ + if (current_depth >= MAX_PACKAGE_DEPTH-1) { + /* + * Too many nested levels of packages + * for us to handle + */ + return (AE_LIMIT); + } + + /* + * Build the package object + */ + this_external_obj->type = ACPI_TYPE_PACKAGE; + this_external_obj->package.count = + this_internal_obj->package.count; + this_external_obj->package.elements = + (ACPI_OBJECT *) free_space; + + /* + * Save space for the array of objects (Package elements) + * update the buffer length counter + */ + object_space = (u32) ROUND_UP_TO_NATIVE_WORD ( + this_external_obj->package.count * + sizeof (ACPI_OBJECT)); + + free_space += object_space; + length += object_space; + + current_depth++; + level_ptr = &level[current_depth]; + level_ptr->internal_obj = this_internal_obj; + level_ptr->external_obj = this_external_obj; + level_ptr->index = 0; + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_external_object + * + * PARAMETERS: *Internal_obj - The internal object to be converted + * *Buffer_ptr - Where the object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to build an API object to be returned to + * the caller. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_external_object ( + ACPI_OBJECT_INTERNAL *internal_obj, + ACPI_BUFFER *ret_buffer) +{ + ACPI_STATUS status; + + + if (IS_THIS_OBJECT_TYPE (internal_obj, ACPI_TYPE_PACKAGE)) { + /* + * Package objects contain other objects (which can be objects) + * buildpackage does it all + */ + status = + acpi_cm_build_external_package_object (internal_obj, + ret_buffer->pointer, + &ret_buffer->length); + } + + else { + /* + * Build a simple object (no nested objects) + */ + status = + acpi_cm_build_external_simple_object (internal_obj, + (ACPI_OBJECT *) ret_buffer->pointer, + ((u8 *) ret_buffer->pointer + + ROUND_UP_TO_NATIVE_WORD ( + sizeof (ACPI_OBJECT))), + &ret_buffer->length); + /* + * build simple does not include the object size in the length + * so we add it in here + */ + ret_buffer->length += sizeof (ACPI_OBJECT); + } + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_internal_simple_object + * + * PARAMETERS: *External_obj - The external object to be converted + * *Internal_obj - Where the internal object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function copies an external object to an internal one. + * NOTE: Pointers can be copied, we don't need to copy data. + * (The pointers have to be valid in our address space no matter + * what we do with them!) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_internal_simple_object ( + ACPI_OBJECT *external_obj, + ACPI_OBJECT_INTERNAL *internal_obj) +{ + + + internal_obj->common.type = (u8) external_obj->type; + + switch (external_obj->type) + { + + case ACPI_TYPE_STRING: + + internal_obj->string.length = external_obj->string.length; + internal_obj->string.pointer = external_obj->string.pointer; + break; + + + case ACPI_TYPE_BUFFER: + + internal_obj->buffer.length = external_obj->buffer.length; + internal_obj->buffer.pointer = external_obj->buffer.pointer; + break; + + + case ACPI_TYPE_NUMBER: + /* + * Number is included in the object itself + */ + internal_obj->number.value = external_obj->number.value; + break; + + + default: + return (AE_CTRL_RETURN_VALUE); + break; + } + + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_internal_package_object + * + * PARAMETERS: *Internal_obj - Pointer to the object we are returning + * *Buffer - Where the object is returned + * *Space_used - Where the length of the object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to place a package object in a user + * buffer. A package object by definition contains other objects. + * + * The buffer is assumed to have sufficient space for the object. + * The caller must have verified the buffer length needed using the + * Acpi_cm_get_object_size function before calling this function. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_internal_package_object ( + ACPI_OBJECT_INTERNAL *internal_obj, + u8 *buffer, + u32 *space_used) +{ + u8 *free_space; + ACPI_OBJECT *external_obj; + u32 current_depth = 0; + ACPI_STATUS status = AE_OK; + u32 length = 0; + u32 this_index; + u32 object_space = 0; + ACPI_OBJECT_INTERNAL *this_internal_obj; + ACPI_OBJECT *this_external_obj; + PKG_SEARCH_INFO *level_ptr; + + + /* + * First package at head of the buffer + */ + external_obj = (ACPI_OBJECT *)buffer; + + /* + * Free space begins right after the first package + */ + free_space = buffer + sizeof(ACPI_OBJECT); + + + /* + * Initialize the working variables + */ + + MEMSET ((void *) level, 0, sizeof(level)); + + level[0].internal_obj = internal_obj; + level[0].external_obj = external_obj; + level_ptr = &level[0]; + current_depth = 0; + + external_obj->type = internal_obj->common.type; + external_obj->package.count = internal_obj->package.count; + external_obj->package.elements = (ACPI_OBJECT *)free_space; + + + /* + * Build an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + + free_space += external_obj->package.count * sizeof(ACPI_OBJECT); + + + while (1) { + this_index = level_ptr->index; + + this_internal_obj = (ACPI_OBJECT_INTERNAL *) + &level_ptr->internal_obj->package.elements[this_index]; + + this_external_obj = (ACPI_OBJECT *) + &level_ptr->external_obj->package.elements[this_index]; + + if (IS_THIS_OBJECT_TYPE (this_internal_obj, ACPI_TYPE_PACKAGE)) { + /* + * If this object is a package then we go one deeper + */ + if (current_depth >= MAX_PACKAGE_DEPTH-1) { + /* + * Too many nested levels of packages for us to handle + */ + return (AE_LIMIT); + } + + /* + * Build the package object + */ + this_external_obj->type = ACPI_TYPE_PACKAGE; + this_external_obj->package.count = this_internal_obj->package.count; + this_external_obj->package.elements = (ACPI_OBJECT *) free_space; + + /* + * Save space for the array of objects (Package elements) + * update the buffer length counter + */ + object_space = this_external_obj->package.count * + sizeof (ACPI_OBJECT); + + free_space += object_space; + length += object_space; + + current_depth++; + level_ptr = &level[current_depth]; + level_ptr->internal_obj = this_internal_obj; + level_ptr->external_obj = this_external_obj; + level_ptr->index = 0; + + } /* if object is a package */ + + else { +/* Status = Acpi_cm_build_simple_object(This_internal_obj, + This_external_obj, Free_space, + &Object_space); +*/ + if (status != AE_OK) { + /* + * Failure get out + */ + return (status); + } + + free_space += object_space; + length += object_space; + + level_ptr->index++; + while (level_ptr->index >= + level_ptr->internal_obj->package.count) + { + /* + * We've handled all of the objects at + * this level, This means that we have + * just completed a package. That package + * may have contained one or more packages + * itself + */ + if (current_depth == 0) { + /* + * We have handled all of the objects + * in the top level package just add + * the length of the package objects + * and get out + */ + *space_used = length; + return (AE_OK); + } + + /* + * go back up a level and move the index + * past the just completed package object. + */ + current_depth--; + level_ptr = &level[current_depth]; + level_ptr->index++; + } + } /* else object is NOT a package */ + } /* while (1) */ + + + /* + * We'll never get here, but the compiler whines about + * return value + */ + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_build_internal_object + * + * PARAMETERS: *Internal_obj - The external object to be converted + * *Buffer_ptr - Where the internal object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: Converts an external object to an internal object. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_build_internal_object ( + ACPI_OBJECT *external_obj, + ACPI_OBJECT_INTERNAL *internal_obj) +{ + ACPI_STATUS status; + + + if (external_obj->type == ACPI_TYPE_PACKAGE) { + /* + * Package objects contain other objects (which can be objects) + * buildpackage does it all + */ +/* + Status = Acpi_cm_build_internal_package_object(Internal_obj, + Ret_buffer->Pointer, + &Ret_buffer->Length); +*/ + return (AE_NOT_IMPLEMENTED); + } + + else { + /* + * Build a simple object (no nested objects) + */ + status = acpi_cm_build_internal_simple_object (external_obj, internal_obj); + /* + * build simple does not include the object size in the length + * so we add it in here + */ + } + + return (status); +} + diff --git a/drivers/acpi/common/cmdebug.c b/drivers/acpi/common/cmdebug.c new file mode 100644 index 000000000..5c0d31c45 --- /dev/null +++ b/drivers/acpi/common/cmdebug.c @@ -0,0 +1,570 @@ + +/****************************************************************************** + * + * Module Name: cmdebug - Debug print routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmdebug"); + + +/***************************************************************************** + * + * FUNCTION: Get/Set debug level + * + * DESCRIPTION: Get or set value of the debug flag + * + * These are used to allow user's to get/set the debug level + * + ****************************************************************************/ + + +s32 +get_debug_level (void) +{ + + return acpi_dbg_level; +} + +void +set_debug_level ( + s32 new_debug_level) +{ + + acpi_dbg_level = new_debug_level; +} + + +/***************************************************************************** + * + * FUNCTION: Function_trace + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level + * + ****************************************************************************/ + +void +function_trace ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name) +{ + + acpi_gbl_nesting_level++; + + debug_print (module_name, line_number, component_id, + TRACE_FUNCTIONS, + " %2.2ld Entered Function: %s\n", + acpi_gbl_nesting_level, function_name); +} + + +/***************************************************************************** + * + * FUNCTION: Function_trace_ptr + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * Pointer - Pointer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level + * + ****************************************************************************/ + +void +function_trace_ptr ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + void *pointer) +{ + + acpi_gbl_nesting_level++; + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Entered Function: %s, 0x%p\n", + acpi_gbl_nesting_level, function_name, pointer); +} + + +/***************************************************************************** + * + * FUNCTION: Function_trace_str + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * String - Additional string to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level + * + ****************************************************************************/ + +void +function_trace_str ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + char *string) +{ + + acpi_gbl_nesting_level++; + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Entered Function: %s, %s\n", + acpi_gbl_nesting_level, function_name, string); +} + + +/***************************************************************************** + * + * FUNCTION: Function_trace_u32 + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * Integer - Integer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level + * + ****************************************************************************/ + +void +function_trace_u32 ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + u32 integer) +{ + + acpi_gbl_nesting_level++; + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Entered Function: %s, 0x%l_x\n", + acpi_gbl_nesting_level, function_name, integer); +} + + +/***************************************************************************** + * + * FUNCTION: Function_exit + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level + * + ****************************************************************************/ + +void +function_exit ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name) +{ + + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Exiting Function: %s\n", + acpi_gbl_nesting_level, function_name); + + acpi_gbl_nesting_level--; +} + + +/***************************************************************************** + * + * FUNCTION: Function_status_exit + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * Status - Exit status code + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level. Prints exit status also. + * + ****************************************************************************/ + +void +function_status_exit ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + ACPI_STATUS status) +{ + + if (status > ACPI_MAX_STATUS) { + debug_print (module_name, line_number, component_id, + TRACE_FUNCTIONS, + " %2.2ld Exiting Function: %s, [Unknown Status] 0x%X\n", + acpi_gbl_nesting_level, + function_name, + status); + } + + else { + debug_print (module_name, line_number, component_id, + TRACE_FUNCTIONS, + " %2.2ld Exiting Function: %s, %s\n", + acpi_gbl_nesting_level, + function_name, + acpi_cm_format_exception (status)); + } + + acpi_gbl_nesting_level--; +} + + +/***************************************************************************** + * + * FUNCTION: Function_value_exit + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * Value - Value to be printed with exit msg + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level. Prints exit value also. + * + ****************************************************************************/ + +void +function_value_exit ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + NATIVE_UINT value) +{ + + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Exiting Function: %s, 0x%X\n", + acpi_gbl_nesting_level, function_name, value); + + acpi_gbl_nesting_level--; +} + + +/***************************************************************************** + * + * FUNCTION: Function_ptr_exit + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Function_name - Name of Caller's function + * Value - Value to be printed with exit msg + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in Debug_level. Prints exit value also. + * + ****************************************************************************/ + +void +function_ptr_exit ( + char *module_name, + s32 line_number, + s32 component_id, + char *function_name, + char *ptr) +{ + + debug_print (module_name, line_number, component_id, TRACE_FUNCTIONS, + " %2.2ld Exiting Function: %s, 0x%p\n", + acpi_gbl_nesting_level, function_name, ptr); + + acpi_gbl_nesting_level--; +} + + +/***************************************************************************** + * + * FUNCTION: Debug_print + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Print_level - Requested debug print level + * Format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message with prefix consisting of the module name, + * line number, and component ID. + * + ****************************************************************************/ + +void +debug_print ( + char *module_name, + s32 line_number, + s32 component_id, + s32 print_level, + char *format, + ...) +{ + va_list args; + + + /* Both the level and the component must be enabled */ + + if ((print_level & acpi_dbg_level) && + (component_id & acpi_dbg_layer)) + { + va_start (args, format); + + acpi_os_printf ("%8s-%04d: ", module_name, line_number); + acpi_os_vprintf (format, args); + } +} + + +/***************************************************************************** + * + * FUNCTION: Debug_print_prefix + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * + * RETURN: None + * + * DESCRIPTION: Print the prefix part of an error message, consisting of the + * module name, and line number + * + ****************************************************************************/ + +void +debug_print_prefix ( + char *module_name, + s32 line_number) +{ + + + acpi_os_printf ("%8s-%04d: ", module_name, line_number); +} + + +/***************************************************************************** + * + * FUNCTION: Debug_print_raw + * + * PARAMETERS: Format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message -- without module/line indentifiers + * + ****************************************************************************/ + +void +debug_print_raw ( + char *format, + ...) +{ + va_list args; + + + va_start (args, format); + + acpi_os_vprintf (format, args); + + va_end (args); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_dump_buffer + * + * PARAMETERS: Buffer - Buffer to dump + * Count - Amount to dump, in bytes + * Component_iD - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ****************************************************************************/ + +void +acpi_cm_dump_buffer ( + char *buffer, + u32 count, + u32 display, + s32 component_id) +{ + u32 i = 0; + u32 j; + u32 temp32; + u8 buf_char; + + + /* Only dump the buffer if tracing is enabled */ + + if (!((TRACE_TABLES & acpi_dbg_level) && + (component_id & acpi_dbg_layer))) + { + return; + } + + + /* + * Nasty little dump buffer routine! + */ + while (i < count) { + /* Print current offset */ + + acpi_os_printf ("%05_x ", i); + + + /* Print 16 hex chars */ + + for (j = 0; j < 16;) { + if (i + j >= count) { + acpi_os_printf ("\n"); + return; + } + + /* Make sure that the char doesn't get sign-extended! */ + + switch (display) + { + /* Default is BYTE display */ + + default: + + acpi_os_printf ("%02_x ", + *((u8 *) &buffer[i + j])); + j += 1; + break; + + + case DB_WORD_DISPLAY: + + MOVE_UNALIGNED16_TO_32 (&temp32, + &buffer[i + j]); + acpi_os_printf ("%04_x ", temp32); + j += 2; + break; + + + case DB_DWORD_DISPLAY: + + MOVE_UNALIGNED32_TO_32 (&temp32, + &buffer[i + j]); + acpi_os_printf ("%08_x ", temp32); + j += 4; + break; + + + case DB_QWORD_DISPLAY: + + MOVE_UNALIGNED32_TO_32 (&temp32, + &buffer[i + j]); + acpi_os_printf ("%08_x", temp32); + + MOVE_UNALIGNED32_TO_32 (&temp32, + &buffer[i + j + 4]); + acpi_os_printf ("%08_x ", temp32); + j += 8; + break; + } + } + + + /* + * Print the ASCII equivalent characters + * But watch out for the bad unprintable ones... + */ + + for (j = 0; j < 16; j++) { + if (i + j >= count) { + acpi_os_printf ("\n"); + return; + } + + buf_char = buffer[i + j]; + if ((buf_char > 0x1F && buf_char < 0x2E) || + (buf_char > 0x2F && buf_char < 0x61) || + (buf_char > 0x60 && buf_char < 0x7F)) + { + acpi_os_printf ("%c", buf_char); + } + else { + acpi_os_printf ("."); + } + } + + /* Done with that line. */ + + acpi_os_printf ("\n"); + i += 16; + } + + return; +} + + diff --git a/drivers/acpi/common/cmdelete.c b/drivers/acpi/common/cmdelete.c new file mode 100644 index 000000000..fc32e3cec --- /dev/null +++ b/drivers/acpi/common/cmdelete.c @@ -0,0 +1,584 @@ + +/****************************************************************************** + * + * Module Name: cmdelete - object deletion and reference count utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "parser.h" + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmdelete"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_delete_internal_obj + * + * PARAMETERS: *Object - Pointer to the list to be deleted + * + * RETURN: None + * + * DESCRIPTION: Low level object deletion, after reference counts have been + * updated (All reference counts, including sub-objects!) + * + ******************************************************************************/ + +void +acpi_cm_delete_internal_obj ( + ACPI_OBJECT_INTERNAL *object) +{ + void *obj_pointer = NULL; + + + if (!object) { + return; + } + + /* + * Must delete or free any pointers within the object that are not + * actual ACPI objects (for example, a raw buffer pointer). + */ + + switch (object->common.type) + { + + case ACPI_TYPE_STRING: + + /* Free the actual string buffer */ + + obj_pointer = object->string.pointer; + break; + + + case ACPI_TYPE_BUFFER: + + /* Free the actual buffer */ + + obj_pointer = object->buffer.pointer; + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * Elements of the package are not handled here, they are deleted + * separately + */ + + /* Free the (variable length) element pointer array */ + + obj_pointer = object->package.elements; + break; + + + case ACPI_TYPE_MUTEX: + + acpi_os_delete_semaphore (object->mutex.semaphore); + break; + + + case ACPI_TYPE_EVENT: + + acpi_os_delete_semaphore (object->event.semaphore); + object->event.semaphore = NULL; + break; + + + case ACPI_TYPE_METHOD: + + /* Delete parse tree if it exists */ + + if (object->method.parser_op) { + acpi_ps_delete_parse_tree (object->method.parser_op); + object->method.parser_op = NULL; + } + + /* Delete semaphore if it exists */ + + if (object->method.semaphore) { + acpi_os_delete_semaphore (object->method.semaphore); + object->method.semaphore = NULL; + } + + break; + + + default: + break; + } + + + /* + * Delete any allocated memory found above + */ + + if (obj_pointer) { + if (!acpi_tb_system_table_pointer (obj_pointer)) { + acpi_cm_free (obj_pointer); + } + } + + + /* Only delete the object if it was dynamically allocated */ + + + if (!(object->common.flags & AO_STATIC_ALLOCATION)) { + acpi_cm_delete_object_desc (object); + + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_delete_internal_object_list + * + * PARAMETERS: *Obj_list - Pointer to the list to be deleted + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function deletes an internal object list, including both + * simple objects and package objects + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_delete_internal_object_list ( + ACPI_OBJECT_INTERNAL **obj_list) +{ + ACPI_OBJECT_INTERNAL **internal_obj; + + + /* Walk the null-terminated internal list */ + + for (internal_obj = obj_list; *internal_obj; internal_obj++) { + /* + * Check for a package + * Simple objects are simply stored in the array and do not + * need to be deleted separately. + */ + + if (IS_THIS_OBJECT_TYPE ((*internal_obj), ACPI_TYPE_PACKAGE)) { + /* Delete the package */ + + /* + * TBD: [Investigate] This might not be the right thing to do, + * depending on how the internal package object was allocated!!! + */ + acpi_cm_delete_internal_obj (*internal_obj); + } + + } + + /* Free the combined parameter pointer list and object array */ + + acpi_cm_free (obj_list); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_update_ref_count + * + * PARAMETERS: *Object - Object whose ref count is to be updated + * Count - Current ref count + * Action - What to do + * + * RETURN: New ref count + * + * DESCRIPTION: Modify the ref count and return it. + * + ******************************************************************************/ + +void +acpi_cm_update_ref_count ( + ACPI_OBJECT_INTERNAL *object, + s32 action) +{ + u16 count; + u16 new_count; + + + if (!object) { + return; + } + + + count = object->common.reference_count; + new_count = count; + + /* + * Reference count action (increment, decrement, or force delete) + */ + + switch (action) + { + + case REF_INCREMENT: + + new_count++; + object->common.reference_count = new_count; + + break; + + + case REF_DECREMENT: + + if (count < 1) { + new_count = 0; + } + + else { + new_count--; + + } + + + object->common.reference_count = new_count; + if (new_count == 0) { + acpi_cm_delete_internal_obj (object); + } + + break; + + + case REF_FORCE_DELETE: + + new_count = 0; + object->common.reference_count = new_count; + acpi_cm_delete_internal_obj (object); + break; + + + default: + + break; + } + + + /* + * Sanity check the reference count, for debug purposes only. + * (A deleted object will have a huge reference count) + */ + + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_update_object_reference + * + * PARAMETERS: *Object - Increment ref count for this object + * and all sub-objects + * Action - Either REF_INCREMENT or REF_DECREMENT or + * REF_FORCE_DELETE + * + * RETURN: Status + * + * DESCRIPTION: Increment the object reference count + * + * Object references are incremented when: + * 1) An object is added as a value in an Name Table Entry (NTE) + * 2) An object is copied (all subobjects must be incremented) + * + * Object references are decremented when: + * 1) An object is removed from an NTE + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_update_object_reference ( + ACPI_OBJECT_INTERNAL *object, + u16 action) +{ + ACPI_STATUS status; + u32 i; + ACPI_OBJECT_INTERNAL *next; + ACPI_OBJECT_INTERNAL *new; + ACPI_GENERIC_STATE *state_list = NULL; + ACPI_GENERIC_STATE *state; + + + /* Ignore a null object ptr */ + + if (!object) { + return (AE_OK); + } + + + /* + * Make sure that this isn't a namespace handle or an AML pointer + */ + + if (VALID_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_NAMED)) { + return (AE_OK); + } + + if (acpi_tb_system_table_pointer (object)) { + return (AE_OK); + } + + + state = acpi_cm_create_update_state (object, action); + + while (state) { + + object = state->update.object; + action = state->update.value; + acpi_cm_delete_generic_state (state); + + /* + * All sub-objects must have their reference count incremented also. + * Different object types have different subobjects. + */ + switch (object->common.type) + { + + case ACPI_TYPE_DEVICE: + + status = acpi_cm_create_update_state_and_push (object->device.addr_handler, + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + + acpi_cm_update_ref_count (object->device.sys_handler, action); + acpi_cm_update_ref_count (object->device.drv_handler, action); + break; + + + case INTERNAL_TYPE_ADDRESS_HANDLER: + + /* Must walk list of address handlers */ + + next = object->addr_handler.link; + while (next) { + new = next->addr_handler.link; + acpi_cm_update_ref_count (next, action); + + next = new; + } + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * We must update all the sub-objects of the package + * (Each of whom may have their own sub-objects, etc. + */ + for (i = 0; i < object->package.count; i++) { + /* + * Push each element onto the stack for later processing. + * Note: There can be null elements within the package, + * these are simply ignored + */ + + status = + acpi_cm_create_update_state_and_push (object->package.elements[i], + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + } + break; + + + case ACPI_TYPE_FIELD_UNIT: + + status = + acpi_cm_create_update_state_and_push (object->field_unit.container, + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + break; + + + case INTERNAL_TYPE_DEF_FIELD: + + status = + acpi_cm_create_update_state_and_push (object->field.container, + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + break; + + + case INTERNAL_TYPE_BANK_FIELD: + + status = + acpi_cm_create_update_state_and_push (object->bank_field.bank_select, + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + + status = + acpi_cm_create_update_state_and_push (object->bank_field.container, + action, &state_list); + if (ACPI_FAILURE (status)) { + return (status); + } + break; + + + case ACPI_TYPE_REGION: + + acpi_cm_update_ref_count (object->region.method, action); + + /* TBD: [Investigate] + Acpi_cm_update_ref_count (Object->Region.Addr_handler, Action); + */ +/* + Status = + Acpi_cm_create_update_state_and_push (Object->Region.Addr_handler, + Action, &State_list); + if (ACPI_FAILURE (Status)) { + return (Status); + } +*/ + break; + + + case INTERNAL_TYPE_REFERENCE: + + break; + } + + + /* + * Now we can update the count in the main object. This can only + * happen after we update the sub-objects in case this causes the + * main object to be deleted. + */ + + acpi_cm_update_ref_count (object, action); + + + /* Move on to the next object to be updated */ + + state = acpi_cm_pop_generic_state (&state_list); + } + + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_add_reference + * + * PARAMETERS: *Object - Object whose reference count is to be + * incremented + * + * RETURN: None + * + * DESCRIPTION: Add one reference to an ACPI object + * + ******************************************************************************/ + +void +acpi_cm_add_reference ( + ACPI_OBJECT_INTERNAL *object) +{ + + + /* + * Ensure that we have a valid object + */ + + if (!acpi_cm_valid_internal_object (object)) { + return; + } + + + /* + * We have a valid ACPI internal object, now increment the reference count + */ + + acpi_cm_update_object_reference (object, REF_INCREMENT); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_remove_reference + * + * PARAMETERS: *Object - Object whose ref count will be decremented + * + * RETURN: None + * + * DESCRIPTION: Decrement the reference count of an ACPI internal object + * + ******************************************************************************/ + +void +acpi_cm_remove_reference ( + ACPI_OBJECT_INTERNAL *object) +{ + + + /* + * Ensure that we have a valid object + */ + + if (!acpi_cm_valid_internal_object (object)) { + return; + } + + /* + * Decrement the reference count, and only actually delete the object + * if the reference count becomes 0. (Must also decrement the ref count + * of all subobjects!) + */ + + acpi_cm_update_object_reference (object, REF_DECREMENT); + + /* + * If the reference count has reached zero, + * delete the object and all sub-objects contained within it + */ +/* + if (Object->Common.Reference_count == 0) { + Acpi_cm_delete_internal_obj (Object); + } +*/ + return; +} + + diff --git a/drivers/acpi/common/cmeval.c b/drivers/acpi/common/cmeval.c new file mode 100644 index 000000000..4692eced9 --- /dev/null +++ b/drivers/acpi/common/cmeval.c @@ -0,0 +1,310 @@ + +/****************************************************************************** + * + * Module Name: cmeval - Object evaluation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "namesp.h" +#include "interp.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmeval"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_evaluate_numeric_object + * + * PARAMETERS: Acpi_device - NTE for the device + * *Address - Where the value is returned + * + * RETURN: Status + * + * DESCRIPTION: evaluates a numeric namespace object for a selected device + * and stores results in *Address. + * + * NOTE: Internal function, no parameter validation + * + ***************************************************************************/ + +ACPI_STATUS +acpi_cm_evaluate_numeric_object ( + char *object_name, + ACPI_NAMED_OBJECT *acpi_device, + u32 *address) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Execute the method */ + + status = acpi_ns_evaluate_relative (acpi_device, object_name, NULL, &obj_desc); + if (ACPI_FAILURE (status)) { + + return (status); + } + + + /* Did we get a return object? */ + + if (!obj_desc) { + return (AE_TYPE); + } + + /* Is the return object of the correct type? */ + + if (obj_desc->common.type != ACPI_TYPE_NUMBER) { + status = AE_TYPE; + } + else { + /* + * Since the structure is a union, setting any field will set all + * of the variables in the union + */ + *address = obj_desc->number.value; + } + + /* On exit, we must delete the return object */ + + acpi_cm_remove_reference (obj_desc); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_execute_HID + * + * PARAMETERS: Acpi_device - NTE for the device + * *Hid - Where the HID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _HID control method that returns the hardware + * ID of the device. + * + * NOTE: Internal function, no parameter validation + * + ***************************************************************************/ + +ACPI_STATUS +acpi_cm_execute_HID ( + ACPI_NAMED_OBJECT *acpi_device, + DEVICE_ID *hid) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Execute the method */ + + status = acpi_ns_evaluate_relative (acpi_device, + METHOD_NAME__HID, NULL, &obj_desc); + if (ACPI_FAILURE (status)) { + + + return (status); + } + + /* Did we get a return object? */ + + if (!obj_desc) { + return (AE_TYPE); + } + + /* + * A _HID can return either a Number (32 bit compressed EISA ID) or + * a string + */ + + if ((obj_desc->common.type != ACPI_TYPE_NUMBER) && + (obj_desc->common.type != ACPI_TYPE_STRING)) + { + status = AE_TYPE; + } + + else { + if (obj_desc->common.type == ACPI_TYPE_NUMBER) { + /* Convert the Numeric HID to string */ + + acpi_aml_eisa_id_to_string (obj_desc->number.value, hid->data.buffer); + hid->type = STRING_DEVICE_ID; + } + + else { + /* Copy the String HID from the returned object */ + + hid->data.string_ptr = obj_desc->string.pointer; + hid->type = STRING_PTR_DEVICE_ID; + } + } + + + /* On exit, we must delete the return object */ + + acpi_cm_remove_reference (obj_desc); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_execute_UID + * + * PARAMETERS: Acpi_device - NTE for the device + * *Uid - Where the UID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _UID control method that returns the hardware + * ID of the device. + * + * NOTE: Internal function, no parameter validation + * + ***************************************************************************/ + +ACPI_STATUS +acpi_cm_execute_UID ( + ACPI_NAMED_OBJECT *acpi_device, + DEVICE_ID *uid) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Execute the method */ + + status = acpi_ns_evaluate_relative (acpi_device, + METHOD_NAME__UID, NULL, &obj_desc); + if (ACPI_FAILURE (status)) { + + + return (status); + } + + /* Did we get a return object? */ + + if (!obj_desc) { + return (AE_TYPE); + } + + /* + * A _UID can return either a Number (32 bit compressed EISA ID) or + * a string + */ + + if ((obj_desc->common.type != ACPI_TYPE_NUMBER) && + (obj_desc->common.type != ACPI_TYPE_STRING)) + { + status = AE_TYPE; + } + + else { + if (obj_desc->common.type == ACPI_TYPE_NUMBER) { + /* Convert the Numeric HID to string */ + + uid->data.number = obj_desc->number.value; + } + + else { + /* Copy the String HID from the returned object */ + + uid->data.string_ptr = obj_desc->string.pointer; + uid->type = STRING_PTR_DEVICE_ID; + } + } + + + /* On exit, we must delete the return object */ + + acpi_cm_remove_reference (obj_desc); + + return (status); +} + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_execute_STA + * + * PARAMETERS: Acpi_device - NTE for the device + * *Flags - Where the status flags are returned + * + * RETURN: Status + * + * DESCRIPTION: Executes _STA for selected device and stores results in + * *Flags. + * + * NOTE: Internal function, no parameter validation + * + ***************************************************************************/ + +ACPI_STATUS +acpi_cm_execute_STA ( + ACPI_NAMED_OBJECT *acpi_device, + u32 *flags) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Execute the method */ + + status = acpi_ns_evaluate_relative (acpi_device, + METHOD_NAME__STA, NULL, &obj_desc); + if (ACPI_FAILURE (status)) { + + + return (status); + } + + + /* Did we get a return object? */ + + if (!obj_desc) { + return (AE_TYPE); + } + + /* Is the return object of the correct type? */ + + if (obj_desc->common.type != ACPI_TYPE_NUMBER) { + status = AE_TYPE; + } + + else { + /* Extract the status flags */ + + *flags = obj_desc->number.value; + } + + + /* On exit, we must delete the return object */ + + acpi_cm_remove_reference (obj_desc); + + return (status); +} diff --git a/drivers/acpi/common/cmglobal.c b/drivers/acpi/common/cmglobal.c new file mode 100644 index 000000000..a23061cae --- /dev/null +++ b/drivers/acpi/common/cmglobal.c @@ -0,0 +1,441 @@ + +/****************************************************************************** + * + * Module Name: cmglobal - Global variables for the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define DEFINE_ACPI_GLOBALS + +#include "acpi.h" +#include "events.h" +#include "namesp.h" +#include "interp.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmglobal"); + + +/****************************************************************************** + * + * Static global variable initialization. + * + ******************************************************************************/ + +/* + * We want the debug switches statically initialized so they + * are already set when the debugger is entered. + */ + +/* Debug switch - level and trace mask */ + +u32 acpi_dbg_level = NORMAL_DEFAULT; + +/* Debug switch - layer (component) mask */ + +u32 acpi_dbg_layer = ALL_COMPONENTS; +u32 acpi_gbl_nesting_level = 0; + + +/* Debugger globals */ + +u8 acpi_gbl_db_terminate_threads = FALSE; +u8 acpi_gbl_method_executing = FALSE; + +/* System flags */ + +u32 acpi_gbl_system_flags = 0; +u32 acpi_gbl_startup_flags = 0; + +/* System starts unitialized! */ +u8 acpi_gbl_shutdown = TRUE; + + +/****************************************************************************** + * + * Namespace globals + * + ******************************************************************************/ + + +/* + * Names built-in to the interpreter + * + * Initial values are currently supported only for types String and Number. + * To avoid type punning, both are specified as strings in this table. + */ + +PREDEFINED_NAMES acpi_gbl_pre_defined_names[] = +{ + {"_GPE", INTERNAL_TYPE_DEF_ANY}, + {"_PR_", INTERNAL_TYPE_DEF_ANY}, + {"_SB_", INTERNAL_TYPE_DEF_ANY}, + {"_SI_", INTERNAL_TYPE_DEF_ANY}, + {"_TZ_", INTERNAL_TYPE_DEF_ANY}, + {"_REV", ACPI_TYPE_NUMBER, "2"}, + {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, + {"_GL_", ACPI_TYPE_MUTEX, "0"}, + + /* Table terminator */ + + {NULL, ACPI_TYPE_ANY} +}; + + +/* + * Properties of the ACPI Object Types, both internal and external. + * + * Elements of Acpi_ns_properties are bit significant + * and the table is indexed by values of ACPI_OBJECT_TYPE + */ + +u8 acpi_gbl_ns_properties[] = +{ + NSP_NORMAL, /* 00 Any */ + NSP_NORMAL, /* 01 Number */ + NSP_NORMAL, /* 02 String */ + NSP_NORMAL, /* 03 Buffer */ + NSP_LOCAL, /* 04 Package */ + NSP_NORMAL, /* 05 Field_unit */ + NSP_NEWSCOPE | NSP_LOCAL, /* 06 Device */ + NSP_LOCAL, /* 07 Acpi_event */ + NSP_NEWSCOPE | NSP_LOCAL, /* 08 Method */ + NSP_LOCAL, /* 09 Mutex */ + NSP_LOCAL, /* 10 Region */ + NSP_NEWSCOPE | NSP_LOCAL, /* 11 Power */ + NSP_NEWSCOPE | NSP_LOCAL, /* 12 Processor */ + NSP_NEWSCOPE | NSP_LOCAL, /* 13 Thermal */ + NSP_NORMAL, /* 14 Buffer_field */ + NSP_NORMAL, /* 15 Ddb_handle */ + NSP_NORMAL, /* 16 reserved */ + NSP_NORMAL, /* 17 reserved */ + NSP_NORMAL, /* 18 reserved */ + NSP_NORMAL, /* 19 reserved */ + NSP_NORMAL, /* 20 reserved */ + NSP_NORMAL, /* 21 reserved */ + NSP_NORMAL, /* 22 reserved */ + NSP_NORMAL, /* 23 reserved */ + NSP_NORMAL, /* 24 reserved */ + NSP_NORMAL, /* 25 Def_field */ + NSP_NORMAL, /* 26 Bank_field */ + NSP_NORMAL, /* 27 Index_field */ + NSP_NORMAL, /* 28 Def_field_defn */ + NSP_NORMAL, /* 29 Bank_field_defn */ + NSP_NORMAL, /* 30 Index_field_defn */ + NSP_NORMAL, /* 31 If */ + NSP_NORMAL, /* 32 Else */ + NSP_NORMAL, /* 33 While */ + NSP_NEWSCOPE, /* 34 Scope */ + NSP_LOCAL, /* 35 Def_any */ + NSP_NORMAL, /* 36 Reference */ + NSP_NORMAL, /* 37 Alias */ + NSP_NORMAL, /* 38 Notify */ + NSP_NORMAL, /* 39 Address Handler */ + NSP_NORMAL /* 40 Invalid */ +}; + + +/****************************************************************************** + * + * Table globals + * + ******************************************************************************/ + + +ACPI_TABLE_DESC acpi_gbl_acpi_tables[NUM_ACPI_TABLES]; + + +ACPI_TABLE_SUPPORT acpi_gbl_acpi_table_data[NUM_ACPI_TABLES] = +{ + /* Name, Signature, Signature size, How many allowed?, Supported? Global typed pointer */ + + /* RSDP 0 */ {"RSDP", RSDP_SIG, sizeof (RSDP_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, NULL}, + /* APIC 1 */ {APIC_SIG, APIC_SIG, sizeof (APIC_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, (void **) &acpi_gbl_APIC}, + /* DSDT 2 */ {DSDT_SIG, DSDT_SIG, sizeof (DSDT_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, (void **) &acpi_gbl_DSDT}, + /* FACP 3 */ {FACP_SIG, FACP_SIG, sizeof (FACP_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, (void **) &acpi_gbl_FACP}, + /* FACS 4 */ {FACS_SIG, FACS_SIG, sizeof (FACS_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, (void **) &acpi_gbl_FACS}, + /* PSDT 5 */ {PSDT_SIG, PSDT_SIG, sizeof (PSDT_SIG)-1, ACPI_TABLE_MULTIPLE, AE_OK, NULL}, + /* RSDT 6 */ {RSDT_SIG, RSDT_SIG, sizeof (RSDT_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, NULL}, + /* SSDT 7 */ {SSDT_SIG, SSDT_SIG, sizeof (SSDT_SIG)-1, ACPI_TABLE_MULTIPLE, AE_OK, NULL}, + /* SBST 8 */ {SBST_SIG, SBST_SIG, sizeof (SBST_SIG)-1, ACPI_TABLE_SINGLE, AE_OK, (void **) &acpi_gbl_SBST}, + /* BOOT 9 */ {BOOT_SIG, BOOT_SIG, sizeof (BOOT_SIG)-1, ACPI_TABLE_SINGLE, AE_SUPPORT, NULL} +}; + +ACPI_INIT_DATA acpi_gbl_acpi_init_data; + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_valid_object_type + * + * PARAMETERS: None. + * + * RETURN: TRUE if valid object type + * + * DESCRIPTION: Validate an object type + * + ****************************************************************************/ + +u8 +acpi_cm_valid_object_type ( + u32 type) +{ + + if (type > ACPI_TYPE_MAX) { + if ((type < INTERNAL_TYPE_BEGIN) || + (type > INTERNAL_TYPE_MAX)) + { + return FALSE; + } + } + + return TRUE; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_format_exception + * + * PARAMETERS: Status - Acpi status to be formatted + * + * RETURN: Formatted status string + * + * DESCRIPTION: Convert an ACPI exception to a string + * + ****************************************************************************/ + +char * +acpi_cm_format_exception ( + ACPI_STATUS status) +{ + + if (status > ACPI_MAX_STATUS) { + return "UNKNOWN_STATUS"; + } + + return (acpi_gbl_exception_names [status]); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_allocate_owner_id + * + * PARAMETERS: Id_type - Type of ID (method or table) + * + * DESCRIPTION: Allocate a table or method owner id + * + ***************************************************************************/ + +ACPI_OWNER_ID +acpi_cm_allocate_owner_id ( + u32 id_type) +{ + ACPI_OWNER_ID owner_id = 0xFFFF; + + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + switch (id_type) + { + case OWNER_TYPE_TABLE: + + owner_id = acpi_gbl_next_table_owner_id; + acpi_gbl_next_table_owner_id++; + + if (acpi_gbl_next_table_owner_id == FIRST_METHOD_ID) { + acpi_gbl_next_table_owner_id = FIRST_TABLE_ID; + } + break; + + + case OWNER_TYPE_METHOD: + + owner_id = acpi_gbl_next_method_owner_id; + acpi_gbl_next_method_owner_id++; + + if (acpi_gbl_next_method_owner_id == FIRST_TABLE_ID) { + acpi_gbl_next_method_owner_id = FIRST_METHOD_ID; + } + break; + } + + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + + return (owner_id); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_cm_init_globals + * + * PARAMETERS: none + * + * DESCRIPTION: Init library globals. All globals that require specific + * initialization should be initialized here! + * + ***************************************************************************/ + +void +acpi_cm_init_globals (ACPI_INIT_DATA *init_data) +{ + u32 i; + + + if (init_data) { + MEMCPY (&acpi_gbl_acpi_init_data, init_data, sizeof (ACPI_INIT_DATA)); + } + + else { + MEMSET (&acpi_gbl_acpi_init_data, 0, sizeof (ACPI_INIT_DATA)); + } + + /* ACPI table structure */ + + for (i = 0; i < ACPI_TABLE_MAX; i++) { + acpi_gbl_acpi_tables[i].prev = &acpi_gbl_acpi_tables[i]; + acpi_gbl_acpi_tables[i].next = &acpi_gbl_acpi_tables[i]; + acpi_gbl_acpi_tables[i].pointer = NULL; + acpi_gbl_acpi_tables[i].length = 0; + acpi_gbl_acpi_tables[i].allocation = ACPI_MEM_NOT_ALLOCATED; + acpi_gbl_acpi_tables[i].count = 0; + } + + + /* Address Space handler array */ + + for (i = 0; i < ACPI_MAX_ADDRESS_SPACE; i++) { + acpi_gbl_address_spaces[i].handler = NULL; + acpi_gbl_address_spaces[i].context = NULL; + } + + /* Mutex locked flags */ + + for (i = 0; i < NUM_MTX; i++) { + acpi_gbl_acpi_mutex_info[i].mutex = NULL; + acpi_gbl_acpi_mutex_info[i].locked = FALSE; + acpi_gbl_acpi_mutex_info[i].use_count = 0; + } + + /* Global notify handlers */ + + acpi_gbl_sys_notify.handler = NULL; + acpi_gbl_drv_notify.handler = NULL; + + /* Global "typed" ACPI table pointers */ + + acpi_gbl_RSDP = NULL; + acpi_gbl_RSDT = NULL; + acpi_gbl_FACS = NULL; + acpi_gbl_FACP = NULL; + acpi_gbl_APIC = NULL; + acpi_gbl_DSDT = NULL; + acpi_gbl_SBST = NULL; + + + /* Global Lock support */ + + acpi_gbl_global_lock_acquired = FALSE; + acpi_gbl_global_lock_thread_count = 0; + + /* Miscellaneous variables */ + + acpi_gbl_system_flags = 0; + acpi_gbl_startup_flags = 0; + acpi_gbl_global_lock_set = FALSE; + acpi_gbl_rsdp_original_location = 0; + acpi_gbl_when_to_parse_methods = METHOD_PARSE_CONFIGURATION; + acpi_gbl_cm_single_step = FALSE; + acpi_gbl_db_terminate_threads = FALSE; + acpi_gbl_shutdown = FALSE; + acpi_gbl_ns_lookup_count = 0; + acpi_gbl_ps_find_count = 0; + acpi_gbl_acpi_hardware_present = TRUE; + acpi_gbl_next_table_owner_id = FIRST_TABLE_ID; + acpi_gbl_next_method_owner_id = FIRST_METHOD_ID; + acpi_gbl_debugger_configuration = DEBUGGER_THREADING; + + /* Cache of small "state" objects */ + + acpi_gbl_generic_state_cache = NULL; + acpi_gbl_generic_state_cache_depth = 0; + acpi_gbl_state_cache_requests = 0; + acpi_gbl_state_cache_hits = 0; + + acpi_gbl_parse_cache = NULL; + acpi_gbl_parse_cache_depth = 0; + acpi_gbl_parse_cache_requests = 0; + acpi_gbl_parse_cache_hits = 0; + + acpi_gbl_object_cache = NULL; + acpi_gbl_object_cache_depth = 0; + acpi_gbl_object_cache_requests = 0; + acpi_gbl_object_cache_hits = 0; + + acpi_gbl_walk_state_cache = NULL; + acpi_gbl_walk_state_cache_depth = 0; + acpi_gbl_walk_state_cache_requests = 0; + acpi_gbl_walk_state_cache_hits = 0; + + /* Interpreter */ + + acpi_gbl_buf_seq = 0; + acpi_gbl_named_object_err = FALSE; + + /* Parser */ + + acpi_gbl_parsed_namespace_root = NULL; + + /* Hardware oriented */ + + acpi_gbl_gpe0enable_register_save = NULL; + acpi_gbl_gpe1_enable_register_save = NULL; + acpi_gbl_original_mode = SYS_MODE_UNKNOWN; /* original ACPI/legacy mode */ + acpi_gbl_gpe_registers = NULL; + acpi_gbl_gpe_info = NULL; + + /* Namespace */ + + acpi_gbl_root_name_table.next_table = NULL; + acpi_gbl_root_name_table.parent_entry = NULL; + acpi_gbl_root_name_table.parent_table = NULL; + + acpi_gbl_root_object = acpi_gbl_root_name_table.entries; + + acpi_gbl_root_object->name = ACPI_ROOT_NAME; + acpi_gbl_root_object->data_type = ACPI_DESC_TYPE_NAMED; + acpi_gbl_root_object->type = ACPI_TYPE_ANY; + acpi_gbl_root_object->this_index = 0; + acpi_gbl_root_object->child_table = NULL; + acpi_gbl_root_object->object = NULL; + + /* Memory allocation metrics - compiled out in non-debug mode. */ + + INITIALIZE_ALLOCATION_METRICS(); + + return; +} + + diff --git a/drivers/acpi/common/cminit.c b/drivers/acpi/common/cminit.c new file mode 100644 index 000000000..a4a7c4c0f --- /dev/null +++ b/drivers/acpi/common/cminit.c @@ -0,0 +1,362 @@ + +/****************************************************************************** + * + * Module Name: cminit - Common ACPI subsystem initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" +#include "events.h" +#include "parser.h" +#include "dispatch.h" + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cminit"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_cm_facp_register_error + * + * PARAMETERS: *Register_name - Pointer to string identifying register + * Value - Actual register contents value + * Acpi_test_spec_section - TDS section containing assertion + * Acpi_assertion - Assertion number being tested + * + * RETURN: none + * + * DESCRIPTION: Display failure message and link failure to TDS assertion + * + ******************************************************************************/ + +void +acpi_cm_facp_register_error ( + char *register_name, + u32 value) +{ + + REPORT_ERROR ("Invalid FACP register value"); + +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_hardware_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize and validate various ACPI registers + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_hardware_initialize (void) +{ + ACPI_STATUS status = AE_OK; + s32 index; + + + /* Are we running on the actual hardware */ + + if (!acpi_gbl_acpi_hardware_present) { + /* No, just return */ + + return (AE_OK); + } + + /* We must have the ACPI tables by the time we get here */ + + if (!acpi_gbl_FACP) { + acpi_gbl_restore_acpi_chipset = FALSE; + + return (AE_NO_ACPI_TABLES); + } + + /* Must support *some* mode! */ +/* + if (!(System_flags & SYS_MODES_MASK)) { + Restore_acpi_chipset = FALSE; + + return (AE_ERROR); + } + +*/ + + + switch (acpi_gbl_system_flags & SYS_MODES_MASK) + { + /* Identify current ACPI/legacy mode */ + + case (SYS_MODE_ACPI): + + acpi_gbl_original_mode = SYS_MODE_ACPI; + break; + + + case (SYS_MODE_LEGACY): + + acpi_gbl_original_mode = SYS_MODE_LEGACY; + break; + + + case (SYS_MODE_ACPI | SYS_MODE_LEGACY): + + if (acpi_hw_get_mode () == SYS_MODE_ACPI) { + acpi_gbl_original_mode = SYS_MODE_ACPI; + } + else { + acpi_gbl_original_mode = SYS_MODE_LEGACY; + } + + break; + } + + + if (acpi_gbl_system_flags & SYS_MODE_ACPI) { + /* Target system supports ACPI mode */ + + /* + * The purpose of this block of code is to save the initial state + * of the ACPI event enable registers. An exit function will be + * registered which will restore this state when the application + * exits. The exit function will also clear all of the ACPI event + * status bits prior to restoring the original mode. + * + * The location of the PM1a_evt_blk enable registers is defined as the + * base of PM1a_evt_blk + PM1a_evt_blk_length / 2. Since the spec further + * fully defines the PM1a_evt_blk to be a total of 4 bytes, the offset + * for the enable registers is always 2 from the base. It is hard + * coded here. If this changes in the spec, this code will need to + * be modified. The PM1b_evt_blk behaves as expected. + */ + + acpi_gbl_pm1_enable_register_save = + acpi_os_in16 ((acpi_gbl_FACP->pm1a_evt_blk + 2)); + if (acpi_gbl_FACP->pm1b_evt_blk) { + acpi_gbl_pm1_enable_register_save |= + acpi_os_in16 ((acpi_gbl_FACP->pm1b_evt_blk + 2)); + } + + + /* + * The GPEs behave similarly, except that the length of the register + * block is not fixed, so the buffer must be allocated with malloc + */ + + if (acpi_gbl_FACP->gpe0blk && acpi_gbl_FACP->gpe0blk_len) { + /* GPE0 specified in FACP */ + + acpi_gbl_gpe0enable_register_save = + acpi_cm_allocate (DIV_2 (acpi_gbl_FACP->gpe0blk_len)); + if (!acpi_gbl_gpe0enable_register_save) { + return (AE_NO_MEMORY); + } + + /* Save state of GPE0 enable bits */ + + for (index = 0; index < DIV_2 (acpi_gbl_FACP->gpe0blk_len); index++) { + acpi_gbl_gpe0enable_register_save[index] = + acpi_os_in8 (acpi_gbl_FACP->gpe0blk + + DIV_2 (acpi_gbl_FACP->gpe0blk_len)); + } + } + + else { + acpi_gbl_gpe0enable_register_save = NULL; + } + + if (acpi_gbl_FACP->gpe1_blk && acpi_gbl_FACP->gpe1_blk_len) { + /* GPE1 defined */ + + acpi_gbl_gpe1_enable_register_save = + acpi_cm_allocate (DIV_2 (acpi_gbl_FACP->gpe1_blk_len)); + if (!acpi_gbl_gpe1_enable_register_save) { + return (AE_NO_MEMORY); + } + + /* save state of GPE1 enable bits */ + + for (index = 0; index < DIV_2 (acpi_gbl_FACP->gpe1_blk_len); index++) { + acpi_gbl_gpe1_enable_register_save[index] = + acpi_os_in8 (acpi_gbl_FACP->gpe1_blk + + DIV_2 (acpi_gbl_FACP->gpe1_blk_len)); + } + } + + else { + acpi_gbl_gpe1_enable_register_save = NULL; + } + + + /* + * Verify Fixed ACPI Description Table fields, + * but don't abort on any problems, just display error + */ + + if (acpi_gbl_FACP->pm1_evt_len < 4) { + acpi_cm_facp_register_error ("PM1_EVT_LEN", + (u32) acpi_gbl_FACP->pm1_evt_len); + } + + if (!acpi_gbl_FACP->pm1_cnt_len) { + acpi_cm_facp_register_error ("PM1_CNT_LEN", + (u32) acpi_gbl_FACP->pm1_cnt_len); + } + + if (!acpi_gbl_FACP->pm1a_evt_blk) { + acpi_cm_facp_register_error ("PM1a_EVT_BLK", acpi_gbl_FACP->pm1a_evt_blk); + } + + if (!acpi_gbl_FACP->pm1a_cnt_blk) { + acpi_cm_facp_register_error ("PM1a_CNT_BLK", acpi_gbl_FACP->pm1a_cnt_blk); + } + + if (!acpi_gbl_FACP->pm_tmr_blk) { + acpi_cm_facp_register_error ("PM_TMR_BLK", acpi_gbl_FACP->pm_tmr_blk); + } + + if (acpi_gbl_FACP->pm2_cnt_blk && !acpi_gbl_FACP->pm2_cnt_len) { + acpi_cm_facp_register_error ("PM2_CNT_LEN", + (u32) acpi_gbl_FACP->pm2_cnt_len); + } + + if (acpi_gbl_FACP->pm_tm_len < 4) { + acpi_cm_facp_register_error ("PM_TM_LEN", + (u32) acpi_gbl_FACP->pm_tm_len); + } + + /* length not multiple of 2 */ + if (acpi_gbl_FACP->gpe0blk && (acpi_gbl_FACP->gpe0blk_len & 1)) { + acpi_cm_facp_register_error ("GPE0_BLK_LEN", + (u32) acpi_gbl_FACP->gpe0blk_len); + } + + /* length not multiple of 2 */ + if (acpi_gbl_FACP->gpe1_blk && (acpi_gbl_FACP->gpe1_blk_len & 1)) { + acpi_cm_facp_register_error ("GPE1_BLK_LEN", + (u32) acpi_gbl_FACP->gpe1_blk_len); + } + } + + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free memory allocated for table storage. + * + ******************************************************************************/ + +void +acpi_cm_terminate (void) +{ + + + /* Free global tables, etc. */ + + if (acpi_gbl_gpe0enable_register_save) { + acpi_cm_free (acpi_gbl_gpe0enable_register_save); + } + + if (acpi_gbl_gpe1_enable_register_save) { + acpi_cm_free (acpi_gbl_gpe1_enable_register_save); + } + + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_subsystem_shutdown + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Shutdown the various subsystems. Don't delete the mutex + * objects here -- because the AML debugger may be still running. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_subsystem_shutdown (void) +{ + + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + return (AE_OK); + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + + /* Close the Namespace */ + + acpi_ns_terminate (); + + /* Close the Acpi_event Handling */ + + acpi_ev_terminate (); + + /* Close the globals */ + + acpi_cm_terminate (); + + /* Flush the local cache(s) */ + + acpi_cm_delete_generic_state_cache (); + acpi_cm_delete_object_cache (); + acpi_ds_delete_walk_state_cache (); + + /* Close the Parser */ + + /* TBD: [Restructure] Acpi_ps_terminate () */ + + acpi_ps_delete_parse_cache (); + + /* Debug only - display leftover memory allocation, if any */ + + acpi_cm_dump_current_allocations (ACPI_UINT32_MAX, NULL); + + BREAKPOINT3; + + return (AE_OK); +} + + diff --git a/drivers/acpi/common/cmobject.c b/drivers/acpi/common/cmobject.c new file mode 100644 index 000000000..f22c15cca --- /dev/null +++ b/drivers/acpi/common/cmobject.c @@ -0,0 +1,648 @@ + +/****************************************************************************** + * + * Module Name: cmobject - ACPI object create/delete/size/cache routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "amlcode.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmobject"); + + +/****************************************************************************** + * + * FUNCTION: _Cm_create_internal_object + * + * PARAMETERS: Address - Address of the memory to deallocate + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * Type - ACPI Type of the new object + * + * RETURN: Object - The new object. Null on failure + * + * DESCRIPTION: Create and initialize a new internal object. + * + * NOTE: + * We always allocate the worst-case object descriptor because these + * objects are cached, and we want them to be one-size-satisifies-any-request. + * This in itself may not be the most memory efficient, but the efficiency + * of the object cache should more than make up for this! + * + ******************************************************************************/ + +ACPI_OBJECT_INTERNAL * +_cm_create_internal_object ( + char *module_name, + s32 line_number, + s32 component_id, + OBJECT_TYPE_INTERNAL type) +{ + ACPI_OBJECT_INTERNAL *object; + + + /* Allocate the raw object descriptor */ + + object = _cm_allocate_object_desc (module_name, line_number, component_id); + if (!object) { + /* Allocation failure */ + + return (NULL); + } + + /* Save the object type in the object descriptor */ + + object->common.type = type; + object->common.size = (u8) sizeof (ACPI_OBJECT_INTERNAL); + + /* Init the reference count */ + + object->common.reference_count = 1; + + /* Any per-type initialization should go here */ + + + /* Memory allocation metrics - compiled out in non debug mode. */ + + INCREMENT_OBJECT_METRICS (sizeof (ACPI_OBJECT_INTERNAL)); + + return (object); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_valid_internal_object + * + * PARAMETERS: Operand - Object to be validated + * + * RETURN: Validate a pointer to be an ACPI_OBJECT_INTERNAL + * + *****************************************************************************/ + +u8 +acpi_cm_valid_internal_object ( + void *object) +{ + + /* Check for a null pointer */ + + if (!object) { + return FALSE; + } + + /* Check for a pointer within one of the ACPI tables */ + + if (acpi_tb_system_table_pointer (object)) { + return FALSE; + } + + /* Check the descriptor type field */ + + if (!VALID_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_INTERNAL)) { + /* Not an ACPI internal object, do some further checking */ + + + + + return FALSE; + } + + + /* The object appears to be a valid ACPI_OBJECT_INTERNAL */ + + return TRUE; +} + + +/***************************************************************************** + * + * FUNCTION: _Cm_allocate_object_desc + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: Pointer to newly allocated object descriptor. Null on error + * + * DESCRIPTION: Allocate a new object descriptor. Gracefully handle + * error conditions. + * + ****************************************************************************/ + +void * +_cm_allocate_object_desc ( + char *module_name, + s32 line_number, + s32 component_id) +{ + ACPI_OBJECT_INTERNAL *object; + + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + acpi_gbl_object_cache_requests++; + + /* Check the cache first */ + + if (acpi_gbl_object_cache) { + /* There is an object available, use it */ + + object = acpi_gbl_object_cache; + acpi_gbl_object_cache = object->common.next; + object->common.next = NULL; + + acpi_gbl_object_cache_hits++; + acpi_gbl_object_cache_depth--; + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + + else { + /* The cache is empty, create a new object */ + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + + /* Attempt to allocate new descriptor */ + + object = _cm_callocate (sizeof (ACPI_OBJECT_INTERNAL), component_id, + module_name, line_number); + + if (!object) { + /* Allocation failed */ + + _REPORT_ERROR (module_name, line_number, component_id, + "Could not allocate Object Descriptor"); + + return (NULL); + } + } + + /* Mark the descriptor type */ + + object->common.data_type = ACPI_DESC_TYPE_INTERNAL; + + return (object); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_delete_object_desc + * + * PARAMETERS: Object - Acpi internal object to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Free an ACPI object descriptor or add it to the object cache + * + ****************************************************************************/ + +void +acpi_cm_delete_object_desc ( + ACPI_OBJECT_INTERNAL *object) +{ + + + /* Object must be an ACPI_OBJECT_INTERNAL */ + + if (object->common.data_type != ACPI_DESC_TYPE_INTERNAL) { + return; + } + + /* Make sure that the object isn't already in the cache */ + + if (object->common.next) { + return; + } + + + /* If cache is full, just free this object */ + + if (acpi_gbl_object_cache_depth >= MAX_OBJECT_CACHE_DEPTH) { + /* + * Memory allocation metrics. Call the macro here since we only + * care about dynamically allocated objects. + */ + DECREMENT_OBJECT_METRICS (acpi_gbl_object_cache->common.size); + + acpi_cm_free (object); + return; + } + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + /* Clear the entire object. This is important! */ + + MEMSET (object, 0, sizeof (ACPI_OBJECT_INTERNAL)); + object->common.data_type = ACPI_DESC_TYPE_INTERNAL; + + /* Put the object at the head of the global cache list */ + + object->common.next = acpi_gbl_object_cache; + acpi_gbl_object_cache = object; + acpi_gbl_object_cache_depth++; + + + acpi_cm_release_mutex (ACPI_MTX_CACHES); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_delete_object_cache + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_cm_delete_object_cache ( + void) +{ + ACPI_OBJECT_INTERNAL *next; + + + /* Traverse the global cache list */ + + while (acpi_gbl_object_cache) { + /* Delete one cached state object */ + + next = acpi_gbl_object_cache->common.next; + acpi_gbl_object_cache->common.next = NULL; + + /* + * Memory allocation metrics. Call the macro here since we only + * care about dynamically allocated objects. + */ + DECREMENT_OBJECT_METRICS (acpi_gbl_object_cache->common.size); + + acpi_cm_free (acpi_gbl_object_cache); + acpi_gbl_object_cache = next; + } + + return; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_init_static_object + * + * PARAMETERS: Obj_desc - Pointer to a "static" object - on stack + * or in the data segment. + * + * RETURN: None. + * + * DESCRIPTION: Initialize a static object. Sets flags to disallow dynamic + * deletion of the object. + * + ****************************************************************************/ + +void +acpi_cm_init_static_object ( + ACPI_OBJECT_INTERNAL *obj_desc) +{ + + + if (!obj_desc) { + return; + } + + + /* + * Clear the entire descriptor + */ + MEMSET ((void *) obj_desc, 0, sizeof (ACPI_OBJECT_INTERNAL)); + + + /* + * Initialize the header fields + * 1) This is an ACPI_OBJECT_INTERNAL descriptor + * 2) The size is the full object (worst case) + * 3) The flags field indicates static allocation + * 4) Reference count starts at one (not really necessary since the + * object can't be deleted, but keeps everything sane) + */ + + obj_desc->common.data_type = ACPI_DESC_TYPE_INTERNAL; + obj_desc->common.size = sizeof (ACPI_OBJECT_INTERNAL); + obj_desc->common.flags = AO_STATIC_ALLOCATION; + obj_desc->common.reference_count = 1; + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_get_simple_object_size + * + * PARAMETERS: *Internal_obj - Pointer to the object we are examining + * *Ret_length - Where the length is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to determine the space required to + * contain a simple object for return to an API user. + * + * The length includes the object structure plus any additional + * needed space. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_get_simple_object_size ( + ACPI_OBJECT_INTERNAL *internal_obj, + u32 *obj_length) +{ + u32 length; + ACPI_STATUS status = AE_OK; + + + /* Handle a null object (Could be a uninitialized package element -- which is legal) */ + + if (!internal_obj) { + *obj_length = 0; + return (AE_OK); + } + + + /* Start with the length of the Acpi object */ + + length = sizeof (ACPI_OBJECT); + + if (VALID_DESCRIPTOR_TYPE (internal_obj, ACPI_DESC_TYPE_NAMED)) { + /* Object is an NTE (reference), just return the length */ + + *obj_length = (u32) ROUND_UP_TO_NATIVE_WORD (length); + return (status); + } + + + /* + * The final length depends on the object type + * Strings and Buffers are packed right up against the parent object and + * must be accessed bytewise or there may be alignment problems. + * + * TBD:[Investigate] do strings and buffers require alignment also? + */ + + switch (internal_obj->common.type) + { + + case ACPI_TYPE_STRING: + + length += internal_obj->string.length; + break; + + + case ACPI_TYPE_BUFFER: + + length += internal_obj->buffer.length; + break; + + + case ACPI_TYPE_NUMBER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + + /* + * No extra data for these types + */ + break; + + + case INTERNAL_TYPE_REFERENCE: + + /* + * The only type that should be here is opcode AML_NAMEPATH_OP -- since + * this means an object reference + */ + if (internal_obj->reference.op_code != AML_NAMEPATH_OP) { + status = AE_TYPE; + } + break; + + + default: + + status = AE_TYPE; + break; + } + + + /* + * Account for the space required by the object rounded up to the next + * multiple of the machine word size. This keeps each object aligned + * on a machine word boundary. (preventing alignment faults on some + * machines.) + */ + *obj_length = (u32) ROUND_UP_TO_NATIVE_WORD (length); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_get_package_object_size + * + * PARAMETERS: *Internal_obj - Pointer to the object we are examining + * *Ret_length - Where the length is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to determine the space required to contain + * a package object for return to an API user. + * + * This is moderately complex since a package contains other objects + * including packages. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_get_package_object_size ( + ACPI_OBJECT_INTERNAL *internal_obj, + u32 *obj_length) +{ + + ACPI_OBJECT_INTERNAL *this_internal_obj; + ACPI_OBJECT_INTERNAL *parent_obj[MAX_PACKAGE_DEPTH] = { 0,0,0,0,0 }; + ACPI_OBJECT_INTERNAL *this_parent; + u32 this_index; + u32 index[MAX_PACKAGE_DEPTH] = { 0,0,0,0,0 }; + u32 length = 0; + u32 object_space; + u32 current_depth = 0; + u32 package_count = 1; + ACPI_STATUS status; + + + parent_obj[0] = internal_obj; + + while (1) { + this_parent = parent_obj[current_depth]; + this_index = index[current_depth]; + this_internal_obj = this_parent->package.elements[this_index]; + + + /* + * Check for 1) An unitialized package element. It is completely + * legal to declare a package and leave it uninitialized + * 2) Any type other than a package. Packages are handled + * below. + */ + + if ((!this_internal_obj) || + (!IS_THIS_OBJECT_TYPE (this_internal_obj, ACPI_TYPE_PACKAGE))) + { + /* + * Simple object - just get the size (Null object/entry handled + * also) + */ + + status = + acpi_cm_get_simple_object_size (this_internal_obj, &object_space); + + if (status != AE_OK) { + return (status); + } + + length += object_space; + + index[current_depth]++; + while (index[current_depth] >= + parent_obj[current_depth]->package.count) + { + /* + * We've handled all of the objects at + * this level, This means that we have + * just completed a package. That package + * may have contained one or more packages + * itself. + */ + if (current_depth == 0) { + /* + * We have handled all of the objects + * in the top level package just add the + * length of the package objects and + * get out. Round up to the next machine + * word. + */ + length += + ROUND_UP_TO_NATIVE_WORD ( + sizeof (ACPI_OBJECT)) * + package_count; + + *obj_length = length; + + return (AE_OK); + } + + /* + * Go back up a level and move the index + * past the just completed package object. + */ + current_depth--; + index[current_depth]++; + } + } + + else { + /* + * This object is a package + * -- go one level deeper + */ + package_count++; + if (current_depth < MAX_PACKAGE_DEPTH-1) { + current_depth++; + parent_obj[current_depth] = this_internal_obj; + index[current_depth] = 0; + } + + else { + /* + * Too many nested levels of packages for us + * to handle + */ + + return (AE_LIMIT); + } + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_get_object_size + * + * PARAMETERS: *Internal_obj - Pointer to the object we are examining + * *Ret_length - Where the length will be returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to determine the space required to + * contain an object for return to an API user. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_get_object_size( + ACPI_OBJECT_INTERNAL *internal_obj, + u32 *obj_length) +{ + ACPI_STATUS status; + + + if ((VALID_DESCRIPTOR_TYPE (internal_obj, ACPI_DESC_TYPE_INTERNAL)) && + (IS_THIS_OBJECT_TYPE (internal_obj, ACPI_TYPE_PACKAGE))) + { + status = + acpi_cm_get_package_object_size (internal_obj, obj_length); + } + + else { + status = + acpi_cm_get_simple_object_size (internal_obj, obj_length); + } + + return status; +} + + diff --git a/drivers/acpi/common/cmutils.c b/drivers/acpi/common/cmutils.c new file mode 100644 index 000000000..bec5d4ccb --- /dev/null +++ b/drivers/acpi/common/cmutils.c @@ -0,0 +1,726 @@ +/****************************************************************************** + * + * Module Name: cmutils - common utility procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "events.h" +#include "hardware.h" +#include "namesp.h" +#include "interp.h" +#include "amlcode.h" +#include "debugger.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmutils"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_valid_acpi_name + * + * PARAMETERS: Character - The character to be examined + * + * RETURN: 1 if Character may appear in a name, else 0 + * + * DESCRIPTION: Check for a valid ACPI name. Each character must be one of: + * 1) Upper case alpha + * 2) numeric + * 3) underscore + * + ****************************************************************************/ + +u8 +acpi_cm_valid_acpi_name ( + u32 name) +{ + char *name_ptr = (char *) &name; + u32 i; + + + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (!((name_ptr[i] == '_') || + (name_ptr[i] >= 'A' && name_ptr[i] <= 'Z') || + (name_ptr[i] >= '0' && name_ptr[i] <= '9'))) + { + return FALSE; + } + } + + + return TRUE; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_valid_acpi_character + * + * PARAMETERS: Character - The character to be examined + * + * RETURN: 1 if Character may appear in a name, else 0 + * + * DESCRIPTION: Check for a printable character + * + ****************************************************************************/ + +u8 +acpi_cm_valid_acpi_character ( + char character) +{ + + return ((u8) ((character == '_') || + (character >= 'A' && character <= 'Z') || + (character >= '0' && character <= '9'))); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_mutex_initialize + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Create the system mutex objects. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_cm_mutex_initialize ( + void) +{ + u32 i; + ACPI_STATUS status; + + + /* + * Create each of the predefined mutex objects + */ + for (i = 0; i < NUM_MTX; i++) { + status = acpi_cm_create_mutex (i); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_mutex_terminate + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete all of the system mutex objects. + * + ****************************************************************************/ + +void +acpi_cm_mutex_terminate ( + void) +{ + u32 i; + + + /* + * Delete each predefined mutex object + */ + for (i = 0; i < NUM_MTX; i++) { + acpi_cm_delete_mutex (i); + } + + return; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_create_mutex + * + * PARAMETERS: Mutex_iD - ID of the mutex to be created + * + * RETURN: Status + * + * DESCRIPTION: Create a mutex object. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_cm_create_mutex ( + ACPI_MUTEX_HANDLE mutex_id) +{ + ACPI_STATUS status = AE_OK; + + + if (mutex_id > MAX_MTX) { + return (AE_BAD_PARAMETER); + } + + + if (!acpi_gbl_acpi_mutex_info[mutex_id].mutex) { + status = acpi_os_create_semaphore (1, 1, + &acpi_gbl_acpi_mutex_info[mutex_id].mutex); + acpi_gbl_acpi_mutex_info[mutex_id].locked = FALSE; + acpi_gbl_acpi_mutex_info[mutex_id].use_count = 0; + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_delete_mutex + * + * PARAMETERS: Mutex_iD - ID of the mutex to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Delete a mutex object. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_cm_delete_mutex ( + ACPI_MUTEX_HANDLE mutex_id) +{ + ACPI_STATUS status; + + + if (mutex_id > MAX_MTX) { + return (AE_BAD_PARAMETER); + } + + + status = acpi_os_delete_semaphore (acpi_gbl_acpi_mutex_info[mutex_id].mutex); + + acpi_gbl_acpi_mutex_info[mutex_id].mutex = NULL; + acpi_gbl_acpi_mutex_info[mutex_id].locked = FALSE; + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_acquire_mutex + * + * PARAMETERS: Mutex_iD - ID of the mutex to be acquired + * + * RETURN: Status + * + * DESCRIPTION: Acquire a mutex object. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_cm_acquire_mutex ( + ACPI_MUTEX_HANDLE mutex_id) +{ + ACPI_STATUS status; + + + if (mutex_id > MAX_MTX) { + return (AE_BAD_PARAMETER); + } + + + status = + acpi_os_wait_semaphore (acpi_gbl_acpi_mutex_info[mutex_id].mutex, + 1, WAIT_FOREVER); + + if (ACPI_SUCCESS (status)) { + acpi_gbl_acpi_mutex_info[mutex_id].locked = TRUE; + acpi_gbl_acpi_mutex_info[mutex_id].use_count++; + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_cm_release_mutex + * + * PARAMETERS: Mutex_iD - ID of the mutex to be released + * + * RETURN: Status + * + * DESCRIPTION: Release a mutex object. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_cm_release_mutex ( + ACPI_MUTEX_HANDLE mutex_id) +{ + ACPI_STATUS status; + + + if (mutex_id > MAX_MTX) { + return (AE_BAD_PARAMETER); + } + + + acpi_gbl_acpi_mutex_info[mutex_id].locked = FALSE; /* Mark before unlocking */ + + status = + acpi_os_signal_semaphore (acpi_gbl_acpi_mutex_info[mutex_id].mutex, 1); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_create_update_state_and_push + * + * PARAMETERS: *Object - Object to be added to the new state + * Action - Increment/Decrement + * State_list - List the state will be added to + * + * RETURN: None + * + * DESCRIPTION: Create a new state and push it + * + ******************************************************************************/ + +ACPI_STATUS +acpi_cm_create_update_state_and_push ( + ACPI_OBJECT_INTERNAL *object, + u16 action, + ACPI_GENERIC_STATE **state_list) +{ + ACPI_GENERIC_STATE *state; + + + /* Ignore null objects; these are expected */ + + if (!object) { + return AE_OK; + } + + state = acpi_cm_create_update_state (object, action); + if (!state) { + return AE_NO_MEMORY; + } + + + acpi_cm_push_generic_state (state_list, state); + return AE_OK; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_push_generic_state + * + * PARAMETERS: List_head - Head of the state stack + * State - State object to push + * + * RETURN: Status + * + * DESCRIPTION: Push a state object onto a state stack + * + ******************************************************************************/ + +void +acpi_cm_push_generic_state ( + ACPI_GENERIC_STATE **list_head, + ACPI_GENERIC_STATE *state) +{ + /* Push the state object onto the front of the list (stack) */ + + state->common.next = *list_head; + *list_head = state; + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_pop_generic_state + * + * PARAMETERS: List_head - Head of the state stack + * + * RETURN: Status + * + * DESCRIPTION: Pop a state object from a state stack + * + ******************************************************************************/ + +ACPI_GENERIC_STATE * +acpi_cm_pop_generic_state ( + ACPI_GENERIC_STATE **list_head) +{ + ACPI_GENERIC_STATE *state; + + + /* Remove the state object at the head of the list (stack) */ + + state = *list_head; + if (state) { + /* Update the list head */ + + *list_head = state->common.next; + } + + return (state); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_create_generic_state + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a generic state object. Attempt to obtain one from + * the global state cache; If none available, create a new one. + * + ******************************************************************************/ + +ACPI_GENERIC_STATE * +acpi_cm_create_generic_state (void) +{ + ACPI_GENERIC_STATE *state; + + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + acpi_gbl_state_cache_requests++; + + /* Check the cache first */ + + if (acpi_gbl_generic_state_cache) { + /* There is an object available, use it */ + + state = acpi_gbl_generic_state_cache; + acpi_gbl_generic_state_cache = state->common.next; + state->common.next = NULL; + + acpi_gbl_state_cache_hits++; + acpi_gbl_generic_state_cache_depth--; + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + + else { + /* The cache is empty, create a new object */ + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + + state = acpi_cm_callocate (sizeof (ACPI_GENERIC_STATE)); + } + + /* Initialize */ + + if (state) { + state->common.data_type = ACPI_DESC_TYPE_STATE; + } + + return state; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_create_update_state + * + * PARAMETERS: Object - Initial Object to be installed in the state + * Action - Update action to be performed + * + * RETURN: Status + * + * DESCRIPTION: Create an "Update State" - a flavor of the generic state used + * to update reference counts and delete complex objects such + * as packages. + * + ******************************************************************************/ + +ACPI_GENERIC_STATE * +acpi_cm_create_update_state ( + ACPI_OBJECT_INTERNAL *object, + u16 action) +{ + ACPI_GENERIC_STATE *state; + + + /* Create the generic state object */ + + state = acpi_cm_create_generic_state (); + if (!state) { + return NULL; + } + + /* Init fields specific to the update struct */ + + state->update.object = object; + state->update.value = action; + + return (state); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_create_control_state + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a "Control State" - a flavor of the generic state used + * to support nested IF/WHILE constructs in the AML. + * + ******************************************************************************/ + +ACPI_GENERIC_STATE * +acpi_cm_create_control_state ( + void) +{ + ACPI_GENERIC_STATE *state; + + + /* Create the generic state object */ + + state = acpi_cm_create_generic_state (); + if (!state) { + return NULL; + } + + + /* Init fields specific to the control struct */ + + state->common.state = CONTROL_CONDITIONAL_EXECUTING; + + return (state); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_delete_generic_state + * + * PARAMETERS: State - The state object to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Put a state object back into the global state cache. The object + * is not actually freed at this time. + * + ******************************************************************************/ + +void +acpi_cm_delete_generic_state ( + ACPI_GENERIC_STATE *state) +{ + + /* If cache is full, just free this state object */ + + if (acpi_gbl_generic_state_cache_depth >= MAX_STATE_CACHE_DEPTH) { + acpi_cm_free (state); + } + + /* Otherwise put this object back into the cache */ + + else { + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + /* Clear the state */ + + MEMSET (state, 0, sizeof (ACPI_GENERIC_STATE)); + state->common.data_type = ACPI_DESC_TYPE_STATE; + + /* Put the object at the head of the global cache list */ + + state->common.next = acpi_gbl_generic_state_cache; + acpi_gbl_generic_state_cache = state; + acpi_gbl_generic_state_cache_depth++; + + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_cm_delete_generic_state_cache + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_cm_delete_generic_state_cache ( + void) +{ + ACPI_GENERIC_STATE *next; + + + /* Traverse the global cache list */ + + while (acpi_gbl_generic_state_cache) { + /* Delete one cached state object */ + + next = acpi_gbl_generic_state_cache->common.next; + acpi_cm_free (acpi_gbl_generic_state_cache); + acpi_gbl_generic_state_cache = next; + } + + return; +} + + +/***************************************************************************** + * + * FUNCTION: _Report_error + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print error message from KD table + * + ****************************************************************************/ + +void +_report_error ( + char *module_name, + s32 line_number, + s32 component_id, + char *message) +{ + + debug_print (module_name, line_number, component_id, ACPI_ERROR, + "*** Error: %s\n", message); + +} + + +/***************************************************************************** + * + * FUNCTION: _Report_warning + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print warning message from KD table + * + ****************************************************************************/ + +void +_report_warning ( + char *module_name, + s32 line_number, + s32 component_id, + char *message) +{ + + debug_print (module_name, line_number, component_id, ACPI_WARN, + "*** Warning: %s\n", message); + +} + + +/***************************************************************************** + * + * FUNCTION: _Report_success + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print warning message from KD table + * + ****************************************************************************/ + +void +_report_success ( + char *module_name, + s32 line_number, + s32 component_id, + char *message) +{ + + debug_print (module_name, line_number, component_id, ACPI_OK, + "*** Success: %s\n", message); +} + + +/***************************************************************************** + * + * FUNCTION: _Report_info + * + * PARAMETERS: Module_name - Caller's module name (for error output) + * Line_number - Caller's line number (for error output) + * Component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print information message from KD table + * + ****************************************************************************/ + +void +_report_info ( + char *module_name, + s32 line_number, + s32 component_id, + char *message) +{ + + debug_print (module_name, line_number, component_id, ACPI_INFO, + "*** Info: %s\n", message); + +} + + diff --git a/drivers/acpi/common/cmxface.c b/drivers/acpi/common/cmxface.c new file mode 100644 index 000000000..adb0f4e72 --- /dev/null +++ b/drivers/acpi/common/cmxface.c @@ -0,0 +1,268 @@ +/****************************************************************************** + * + * Module Name: cmxface - External interfaces for "global" ACPI functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "events.h" +#include "hardware.h" +#include "namesp.h" +#include "interp.h" +#include "amlcode.h" +#include "debugger.h" + + +#define _COMPONENT MISCELLANEOUS + MODULE_NAME ("cmxface"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initializes all global variables. This is the first function + * called, so any early initialization belongs here. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_initialize (ACPI_INIT_DATA *init_data) +{ + ACPI_STATUS status; + + + /* Initialize all globals used by the subsystem */ + + acpi_cm_init_globals (init_data); + + /* Initialize the OS-Dependent layer */ + + status = acpi_os_initialize (); + if (ACPI_FAILURE (status)) { + REPORT_ERROR ("OSD Initialization Failure"); + return (status); + } + + /* Create the default mutex objects */ + + status = acpi_cm_mutex_initialize (); + if (ACPI_FAILURE (status)) { + REPORT_ERROR ("Global Mutex Initialization Failure"); + return (status); + } + + /* If configured, initialize the AML debugger */ + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_terminate + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Shutdown the ACPI subsystem. Release all resources. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_terminate (void) +{ + + /* Terminate the AML Debuger if present */ + + acpi_gbl_db_terminate_threads = TRUE; + acpi_cm_release_mutex (ACPI_MTX_DEBUG_CMD_READY); + + + /* Shutdown and free all resources */ + + acpi_cm_subsystem_shutdown (); + + + /* Free the mutex objects */ + + acpi_cm_mutex_terminate (); + + + /* Now we can shutdown the OS-dependent layer */ + + acpi_os_terminate (); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_system_info + * + * PARAMETERS: Out_buffer - a pointer to a buffer to receive the + * resources for the device + * Buffer_length - the number of bytes available in the buffer + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get information about the current + * state of the ACPI subsystem. It will return system information + * in the Out_buffer. + * + * If the function fails an appropriate status will be returned + * and the value of Out_buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_system_info ( + ACPI_BUFFER *out_buffer) +{ + ACPI_SYSTEM_INFO *info_ptr; + u32 i; + + + /* + * Must have a valid buffer + */ + if ((!out_buffer) || + (!out_buffer->pointer)) + { + return (AE_BAD_PARAMETER); + } + + if (out_buffer->length < sizeof (ACPI_SYSTEM_INFO)) { + /* + * Caller's buffer is too small + */ + out_buffer->length = sizeof (ACPI_SYSTEM_INFO); + + return (AE_BUFFER_OVERFLOW); + } + + + /* + * Set return length and get data + */ + out_buffer->length = sizeof (ACPI_SYSTEM_INFO); + info_ptr = (ACPI_SYSTEM_INFO *) out_buffer->pointer; + + /* TBD [Future]: need a version number, or use the version string */ + info_ptr->acpi_ca_version = 0x1234; + + /* System flags (ACPI capabilities) */ + + info_ptr->flags = acpi_gbl_system_flags; + + /* Timer resolution - 24 or 32 bits */ + + info_ptr->timer_resolution = acpi_hw_pmt_resolution (); + + /* Clear the reserved fields */ + + info_ptr->reserved1 = 0; + info_ptr->reserved2 = 0; + + /* Current debug levels */ + + info_ptr->debug_layer = acpi_dbg_layer; + info_ptr->debug_level = acpi_dbg_level; + + /* Current status of the ACPI tables, per table type */ + + info_ptr->num_table_types = NUM_ACPI_TABLES; + for (i = 0; i < NUM_ACPI_TABLES; i++); { + info_ptr->table_info[i].count = acpi_gbl_acpi_tables[i].count; + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_format_exception + * + * PARAMETERS: Out_buffer - a pointer to a buffer to receive the + * exception name + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function translates an ACPI exception into an ASCII string. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_format_exception ( + ACPI_STATUS exception, + ACPI_BUFFER *out_buffer) +{ + u32 length; + char *formatted_exception; + + + /* + * Must have a valid buffer + */ + if ((!out_buffer) || + (!out_buffer->pointer)) + { + return (AE_BAD_PARAMETER); + } + + + /* Exception must be within range */ + + if (exception > ACPI_MAX_STATUS) { + return (AE_BAD_PARAMETER); + } + + + /* Convert the exception code */ + + formatted_exception = acpi_cm_format_exception (exception); + + /* + * Get length of string and check if it will fit in caller's buffer + */ + + length = STRLEN (formatted_exception); + if (out_buffer->length < length) { + out_buffer->length = length; + return (AE_BUFFER_OVERFLOW); + } + + + /* Copy the string, all done */ + + STRCPY (out_buffer->pointer, formatted_exception); + + return (AE_OK); +} + diff --git a/drivers/acpi/dispatcher/dsfield.c b/drivers/acpi/dispatcher/dsfield.c new file mode 100644 index 000000000..a0ca0cb63 --- /dev/null +++ b/drivers/acpi/dispatcher/dsfield.c @@ -0,0 +1,414 @@ + +/****************************************************************************** + * + * Module Name: dsfield - Dispatcher field routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dsfield"); + + +/* + * Field flags: Bits 00 - 03 : Access_type (Any_acc, Byte_acc, etc.) + * 04 : Lock_rule (1 == Lock) + * 05 - 06 : Update_rule + */ + +#define FIELD_ACCESS_TYPE_MASK 0x0F +#define FIELD_LOCK_RULE_MASK 0x10 +#define FIELD_UPDATE_RULE_MASK 0x60 + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * Region - NTE for the containing Operation Region + * + * RETURN: Status + * + * DESCRIPTION: Create a new field in the specified operation region + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status = AE_AML_ERROR; + ACPI_GENERIC_OP *arg; + ACPI_NAMED_OBJECT *entry; + u8 field_flags; + u8 access_attribute = 0; + u32 field_bit_position = 0; + + + /* First arg is the name of the parent Op_region */ + + arg = op->value.arg; + + /* Second arg is the field flags */ + + arg = arg->next; + field_flags = (u8) arg->value.integer; + + /* Each remaining arg is a Named Field */ + + arg = arg->next; + while (arg) { + switch (arg->opcode) + { + case AML_RESERVEDFIELD_OP: + + field_bit_position += arg->value.size; + break; + + + case AML_ACCESSFIELD_OP: + + /* + * Get a new Access_type and Access_attribute for all + * entries (until end or another Access_as keyword) + */ + + access_attribute = (u8) arg->value.integer; + field_flags = (u8) + ((field_flags & FIELD_ACCESS_TYPE_MASK) || + ((u8) (arg->value.integer >> 8))); + break; + + + case AML_NAMEDFIELD_OP: + + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &((ACPI_NAMED_OP *)arg)->name, + INTERNAL_TYPE_DEF_FIELD, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &entry); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Initialize an object for the new NTE that is on + * the object stack + */ + + status = acpi_aml_prep_def_field_value (entry, region, + field_flags, + access_attribute, + field_bit_position, + arg->value.size); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Keep track of bit position for the *next* field */ + + field_bit_position += arg->value.size; + break; + } + + arg = arg->next; + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_bank_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * Region - NTE for the containing Operation Region + * + * RETURN: Status + * + * DESCRIPTION: Create a new bank field in the specified operation region + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_bank_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status = AE_AML_ERROR; + ACPI_GENERIC_OP *arg; + ACPI_NAMED_OBJECT *bank_reg; + ACPI_NAMED_OBJECT *entry; + u32 bank_value; + u8 field_flags; + u8 access_attribute = 0; + u32 field_bit_position = 0; + + + /* First arg is the name of the parent Op_region */ + + arg = op->value.arg; + + /* Socond arg is the Bank Register */ + + arg = arg->next; + + status = acpi_ns_lookup (walk_state->scope_info, arg->value.string, + INTERNAL_TYPE_BANK_FIELD_DEFN, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &bank_reg); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Third arg is the Bank_value */ + + arg = arg->next; + bank_value = arg->value.integer; + + + /* Next arg is the field flags */ + + arg = arg->next; + field_flags = (u8) arg->value.integer; + + /* Each remaining arg is a Named Field */ + + arg = arg->next; + while (arg) { + switch (arg->opcode) + { + case AML_RESERVEDFIELD_OP: + + field_bit_position += arg->value.size; + break; + + + case AML_ACCESSFIELD_OP: + + /* + * Get a new Access_type and Access_attribute for + * all entries (until end or another Access_as keyword) + */ + + access_attribute = (u8) arg->value.integer; + field_flags = (u8) + ((field_flags & FIELD_ACCESS_TYPE_MASK) || + ((u8) (arg->value.integer >> 8))); + break; + + + case AML_NAMEDFIELD_OP: + + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &((ACPI_NAMED_OP *)arg)->name, + INTERNAL_TYPE_DEF_FIELD, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &entry); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Initialize an object for the new NTE that is on + * the object stack + */ + + status = acpi_aml_prep_bank_field_value (entry, region, + bank_reg, bank_value, + field_flags, + access_attribute, + field_bit_position, + arg->value.size); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Keep track of bit position for the *next* field */ + + field_bit_position += arg->value.size; + break; + + } + + arg = arg->next; + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_index_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * Region - NTE for the containing Operation Region + * + * RETURN: Status + * + * DESCRIPTION: Create a new index field in the specified operation region + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_index_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status; + ACPI_GENERIC_OP *arg; + ACPI_NAMED_OBJECT *entry; + ACPI_NAMED_OBJECT *index_reg; + ACPI_NAMED_OBJECT *data_reg; + u8 field_flags; + u8 access_attribute = 0; + u32 field_bit_position = 0; + + + arg = op->value.arg; + + /* First arg is the name of the Index register */ + + status = acpi_ns_lookup (walk_state->scope_info, arg->value.string, + ACPI_TYPE_ANY, IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &index_reg); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Second arg is the data register */ + + arg = arg->next; + + status = acpi_ns_lookup (walk_state->scope_info, arg->value.string, + INTERNAL_TYPE_INDEX_FIELD_DEFN, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &data_reg); + + if (ACPI_FAILURE (status)) { + return (status); + } + + + /* Next arg is the field flags */ + + arg = arg->next; + field_flags = (u8) arg->value.integer; + + + /* Each remaining arg is a Named Field */ + + arg = arg->next; + while (arg) { + switch (arg->opcode) + { + case AML_RESERVEDFIELD_OP: + + field_bit_position += arg->value.size; + break; + + + case AML_ACCESSFIELD_OP: + + /* + * Get a new Access_type and Access_attribute for all + * entries (until end or another Access_as keyword) + */ + + access_attribute = (u8) arg->value.integer; + field_flags = (u8) + ((field_flags & FIELD_ACCESS_TYPE_MASK) || + ((u8) (arg->value.integer >> 8))); + break; + + + case AML_NAMEDFIELD_OP: + + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &((ACPI_NAMED_OP *)arg)->name, + INTERNAL_TYPE_INDEX_FIELD, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &entry); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Initialize an object for the new NTE that is on + * the object stack + */ + + status = acpi_aml_prep_index_field_value (entry, index_reg, + data_reg, field_flags, + access_attribute, + field_bit_position, + arg->value.size); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Keep track of bit position for the *next* field */ + + field_bit_position += arg->value.size; + break; + + + default: + + status = AE_AML_ERROR; + break; + } + + arg = arg->next; + } + + return (status); +} + + diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c new file mode 100644 index 000000000..af55b770b --- /dev/null +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -0,0 +1,506 @@ + +/****************************************************************************** + * + * Module Name: dsmethod - Parser/Interpreter interface - control method parsing + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "debugger.h" + + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dsmethod"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_parse_method + * + * PARAMETERS: Obj_handle - NTE of the method + * Level - Current nesting level + * Context - Points to a method counter + * Return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Call the parser and parse the AML that is + * associated with the method. + * + * MUTEX: Assumes parser is locked + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_parse_method ( + ACPI_HANDLE obj_handle) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_GENERIC_OP *op; + ACPI_NAMED_OBJECT *entry; + ACPI_OWNER_ID owner_id; + + + /* Parameter Validation */ + + if (!obj_handle) { + return (AE_NULL_ENTRY); + } + + + /* Extract the method object from the method NTE */ + + entry = (ACPI_NAMED_OBJECT*) obj_handle; + obj_desc = entry->object; + if (!obj_desc) { + return (AE_NULL_OBJECT); + } + + /* Create a mutex for the method if there is a concurrency limit */ + + if ((obj_desc->method.concurrency != INFINITE_CONCURRENCY) && + (!obj_desc->method.semaphore)) + { + status = acpi_os_create_semaphore (1, + obj_desc->method.concurrency, + &obj_desc->method.semaphore); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + /* + * Allocate a new parser op to be the root of the parsed + * method tree + */ + + op = acpi_ps_alloc_op (AML_METHOD_OP); + if (!op) { + return (AE_NO_MEMORY); + } + + /* Init new op with the method name and pointer back to the NTE */ + + acpi_ps_set_name (op, entry->name); + op->acpi_named_object = entry; + + + /* + * Parse the method, creating a parse tree. + * + * The parse also includes a first pass load of the + * namespace where newly declared named objects are + * added into the namespace. Actual evaluation of + * the named objects (what would be called a "second + * pass") happens during the actual execution of the + * method so that operands to the named objects can + * take on dynamic run-time values. + */ + + status = acpi_ps_parse_aml (op, obj_desc->method.pcode, + obj_desc->method.pcode_length, 0); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Get a new Owner_id for objects created by this method */ + + owner_id = acpi_cm_allocate_owner_id (OWNER_TYPE_METHOD); + + /* Install the parsed tree in the method object */ + + obj_desc->method.parser_op = op; + obj_desc->method.owning_id = owner_id; + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_begin_method_execution + * + * PARAMETERS: Method_entry - NTE of the method + * Obj_desc - The method object + * + * RETURN: Status + * + * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, + * increments the thread count, and waits at the method semaphore + * for clearance to execute. + * + * MUTEX: Locks/unlocks parser. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_begin_method_execution ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (!method_entry) { + return (AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object (method_entry); + if (!obj_desc) { + return (AE_NULL_OBJECT); + } + + /* + * Lock the parser while we check for and possibly parse the + * control method + */ + + acpi_cm_acquire_mutex (ACPI_MTX_PARSER); + + + /* If method is not parsed at this time, we must parse it first */ + + if (!obj_desc->method.parser_op) { + + status = acpi_ds_parse_method (method_entry); + if (ACPI_FAILURE (status)) { + acpi_cm_release_mutex (ACPI_MTX_PARSER); + return (status); + } + } + + + /* + * Increment the method parse tree thread count since there + * is one additional thread executing in it. If configured + * for deletion-on-exit, the parse tree will be deleted when + * the last thread completes execution of the method + */ + + ((ACPI_DEFERRED_OP *) obj_desc->method.parser_op)->thread_count++; + + /* + * Parsing is complete, we can unlock the parser. Parse tree + * cannot be deleted at least until this thread completes. + */ + + acpi_cm_release_mutex (ACPI_MTX_PARSER); + + /* + * If there is a concurrency limit on this method, we need to + * obtain a unit from the method semaphore. This releases the + * interpreter if we block + */ + + if (obj_desc->method.semaphore) { + status = acpi_aml_system_wait_semaphore (obj_desc->method.semaphore, + WAIT_FOREVER); + } + + + return (status); + +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_call_control_method + * + * PARAMETERS: Walk_state - Current state of the walk + * Op - Current Op to be walked + * + * RETURN: Status + * + * DESCRIPTION: Transfer execution to a called control method + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_call_control_method ( + ACPI_WALK_LIST *walk_list, + ACPI_WALK_STATE *this_walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status; + ACPI_DEFERRED_OP *method; + ACPI_NAMED_OBJECT *method_entry; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_WALK_STATE *next_walk_state; + u32 i; + + + /* + * Prev_op points to the METHOD_CALL Op. + * Get the NTE entry (in the METHOD_CALL->NAME Op) and the + * corresponding METHOD Op + */ + + method_entry = (this_walk_state->prev_op->value.arg)->acpi_named_object; + if (!method_entry) { + return (AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object (method_entry); + if (!obj_desc) { + return (AE_NULL_OBJECT); + } + + /* Parse method if necessary, wait on concurrency semaphore */ + + status = acpi_ds_begin_method_execution (method_entry, obj_desc); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Save the (current) Op for when this walk is restarted */ + + this_walk_state->method_call_op = this_walk_state->prev_op; + this_walk_state->prev_op = op; + method = obj_desc->method.parser_op; + + /* Create a new state for the preempting walk */ + + next_walk_state = acpi_ds_create_walk_state (obj_desc->method.owning_id, + (ACPI_GENERIC_OP *) method, + obj_desc, walk_list); + if (!next_walk_state) { + return (AE_NO_MEMORY); + } + + /* The Next_op of the Next_walk will be the beginning of the method */ + + next_walk_state->next_op = (ACPI_GENERIC_OP *) method; + + /* Open a new scope */ + + status = acpi_ds_scope_stack_push (method_entry->child_table, + ACPI_TYPE_METHOD, next_walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + + /* + * Initialize the arguments for the method. The resolved + * arguments were put on the previous walk state's operand + * stack. Operands on the previous walk state stack always + * start at index 0. + */ + + status = acpi_ds_method_data_init_args (&this_walk_state->operands[0], + this_walk_state->num_operands); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * Delete the operands on the previous walkstate operand stack + * (they were copied to new objects) + */ + + for (i = 0; i < obj_desc->method.param_count; i++) { + acpi_cm_remove_reference (this_walk_state->operands [i]); + } + + /* Clear the operand stack */ + + this_walk_state->num_operands = 0; + + + return (AE_OK); + + + /* On error, we must delete the new walk state */ + +cleanup: + acpi_ds_terminate_control_method (next_walk_state); + acpi_ds_delete_walk_state (next_walk_state); + return (status); + +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_restart_control_method + * + * PARAMETERS: Walk_state - State of the method when it was preempted + * Op - Pointer to new current op + * + * RETURN: Status + * + * DESCRIPTION: Restart a method that was preempted + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_restart_control_method ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL *return_desc) +{ + ACPI_STATUS status; + + + if (return_desc) { + /* + * Get the return value (if any) from the previous method. + * NULL if no return value + */ + + status = acpi_ds_result_stack_push (return_desc, walk_state); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (return_desc); + return (status); + } + + /* + * Delete the return value if it will not be used by the + * calling method + */ + + acpi_ds_delete_result_if_not_used (walk_state->method_call_op, + return_desc, walk_state); + } + + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_terminate_control_method + * + * PARAMETERS: Walk_state - State of the method + * + * RETURN: Status + * + * DESCRIPTION: Terminate a control method. Delete everything that the method + * created, delete all locals and arguments, and delete the parse + * tree if requested. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_terminate_control_method ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_DEFERRED_OP *op; + ACPI_NAMED_OBJECT *method_entry; + + + /* The method object should be stored in the walk state */ + + obj_desc = walk_state->method_desc; + if (!obj_desc) { + return (AE_OK); + } + + /* Delete all arguments and locals */ + + acpi_ds_method_data_delete_all (walk_state); + + /* + * Lock the parser while we terminate this method. + * If this is the last thread executing the method, + * we have additional cleanup to perform + */ + + acpi_cm_acquire_mutex (ACPI_MTX_PARSER); + + /* + * The root of the method parse tree should be stored + * in the method object + */ + + op = obj_desc->method.parser_op; + if (!op) { + goto unlock_and_exit; + } + + /* Signal completion of the execution of this method if necessary */ + + if (walk_state->method_desc->method.semaphore) { + status = acpi_os_signal_semaphore ( + walk_state->method_desc->method.semaphore, 1); + } + + /* Decrement the thread count on the method parse tree */ + + op->thread_count--; + if (!op->thread_count) { + /* + * There are no more threads executing this method. Perform + * additional cleanup. + * + * The method NTE is stored in the method Op + */ + method_entry = op->acpi_named_object; + + /* + * Delete any namespace entries created immediately underneath + * the method + */ + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + if (method_entry->child_table) { + acpi_ns_delete_namespace_subtree (method_entry); + } + + /* + * Delete any namespace entries created anywhere else within + * the namespace + */ + + acpi_ns_delete_namespace_by_owner ( + walk_state->method_desc->method.owning_id); + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + /* + * Delete the method's parse tree if asked to + */ + if (acpi_gbl_when_to_parse_methods & METHOD_DELETE_AT_COMPLETION) { + acpi_ps_delete_parse_tree ( + walk_state->method_desc->method.parser_op); + + walk_state->method_desc->method.parser_op = NULL; + } + } + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_PARSER); + return (AE_OK); +} + + diff --git a/drivers/acpi/dispatcher/dsmthdat.c b/drivers/acpi/dispatcher/dsmthdat.c new file mode 100644 index 000000000..a988b8911 --- /dev/null +++ b/drivers/acpi/dispatcher/dsmthdat.c @@ -0,0 +1,682 @@ + +/****************************************************************************** + * + * Module Name: dsmthdat - control method arguments and local variables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" + + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dsmthdat"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_init + * + * PARAMETERS: *Obj_desc + * + * RETURN: Status + * + * DESCRIPTION: Initialize the data structures that hold the method's arguments + * and locals. The data struct is an array of NTEs for each. + * This allows Ref_of and De_ref_of to work properly for these + * special data types. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_init ( + ACPI_WALK_STATE *walk_state) +{ + u32 i; + + + /* + * Walk_state fields are initialized to zero by the + * Acpi_cm_callocate(). + * + * An NTE is assigned to each argument and local so + * that Ref_of() can return a pointer to the NTE. + */ + + /* Init the method arguments */ + + for (i = 0; i < MTH_NUM_ARGS; i++) { + MOVE_UNALIGNED32_TO_32 (&walk_state->arguments[i].name, + NAMEOF_ARG_NTE); + + walk_state->arguments[i].name |= (i << 24); + walk_state->arguments[i].data_type = ACPI_DESC_TYPE_NAMED; + walk_state->arguments[i].type = + INTERNAL_TYPE_METHOD_ARGUMENT; + } + + /* Init the method locals */ + + for (i = 0; i < MTH_NUM_LOCALS; i++) { + MOVE_UNALIGNED32_TO_32 (&walk_state->local_variables[i].name, + NAMEOF_LOCAL_NTE); + + walk_state->local_variables[i].name |= (i << 24); + walk_state->local_variables[i].data_type = ACPI_DESC_TYPE_NAMED; + walk_state->local_variables[i].type = + INTERNAL_TYPE_METHOD_LOCAL_VAR; + } + + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_delete_all + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Delete method locals and arguments. Arguments are only + * deleted if this method was called from another method. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_delete_all ( + ACPI_WALK_STATE *walk_state) +{ + u32 index; + ACPI_OBJECT_INTERNAL *object; + + + /* Delete the locals */ + + for (index = 0; index < MTH_NUM_LOCALS; index++) { + object = walk_state->local_variables[index].object; + if (object) { + /* Remove first */ + walk_state->local_variables[index].object = NULL; + /* Was given a ref when stored */ + acpi_cm_remove_reference (object); + } + } + + + /* Delete the arguments */ + + for (index = 0; index < MTH_NUM_ARGS; index++) { + object = walk_state->arguments[index].object; + if (object) { + /* Remove first */ + walk_state->arguments[index].object = NULL; + /* Was given a ref when stored */ + acpi_cm_remove_reference (object); + } + } + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_init_args + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize arguments for a method + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_init_args ( + ACPI_OBJECT_INTERNAL **params, + u32 max_param_count) +{ + ACPI_STATUS status; + u32 mindex; + u32 pindex; + + + if (!params) { + return (AE_OK); + } + + /* Copy passed parameters into the new method stack frame */ + + for (pindex = mindex = 0; + (mindex < MTH_NUM_ARGS) && (pindex < max_param_count); + mindex++) + { + if (params[pindex]) { + /* + * A valid parameter. + * Set the current method argument to the + * Params[Pindex++] argument object descriptor + */ + status = acpi_ds_method_data_set_value (MTH_TYPE_ARG, + mindex, + params[pindex]); + if (ACPI_FAILURE (status)) { + break; + } + + pindex++; + } + + else { + break; + } + } + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_get_entry + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument to get + * Entry - Pointer to where a pointer to the stack + * entry is returned. + * + * RETURN: Status + * + * DESCRIPTION: Get the address of the stack entry given by Type:Index + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_get_entry ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL ***entry) +{ + ACPI_WALK_STATE *walk_state; + + + walk_state = acpi_ds_get_current_walk_state (acpi_gbl_current_walk_list); + + /* + * Get the requested object. + * The stack "Type" is either a Local_variable or an Argument + */ + + switch (type) + { + + case MTH_TYPE_LOCAL: + + if (index > MTH_MAX_LOCAL) { + return (AE_BAD_PARAMETER); + } + + *entry = + (ACPI_OBJECT_INTERNAL **) &walk_state->local_variables[index].object; + break; + + + case MTH_TYPE_ARG: + + if (index > MTH_MAX_ARG) { + return (AE_BAD_PARAMETER); + } + + *entry = + (ACPI_OBJECT_INTERNAL **) &walk_state->arguments[index].object; + break; + + + default: + return (AE_BAD_PARAMETER); + } + + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_set_entry + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument to get + * Object - Object to be inserted into the stack entry + * + * RETURN: Status + * + * DESCRIPTION: Insert an object onto the method stack at entry Type:Index. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_set_entry ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL *object) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **entry; + + + /* Get a pointer to the stack entry to set */ + + status = acpi_ds_method_data_get_entry (type, index, &entry); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Increment ref count so object can't be deleted while installed */ + + acpi_cm_add_reference (object); + + /* Install the object into the stack entry */ + + *entry = object; + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_get_type + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument whose type + * to get + * + * RETURN: Data type of selected Arg or Local + * Used only in Exec_monadic2()/Type_op. + * + ****************************************************************************/ + +OBJECT_TYPE_INTERNAL +acpi_ds_method_data_get_type ( + u32 type, + u32 index) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **entry; + ACPI_OBJECT_INTERNAL *object; + + + /* Get a pointer to the requested stack entry */ + + status = acpi_ds_method_data_get_entry (type, index, &entry); + if (ACPI_FAILURE (status)) { + return ((ACPI_TYPE_NOT_FOUND)); + } + + /* Get the object from the method stack */ + + object = *entry; + + /* Get the object type */ + + if (!object) { + /* Any == 0 => "uninitialized" -- see spec 15.2.3.5.2.28 */ + return (ACPI_TYPE_ANY); + } + + return (object->common.type); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_get_nte + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument whose type + * to get + * + * RETURN: Get the NTE associated with a local or arg. + * + ****************************************************************************/ + +ACPI_NAMED_OBJECT* +acpi_ds_method_data_get_nte ( + u32 type, + u32 index) +{ + ACPI_NAMED_OBJECT *entry = NULL; + ACPI_WALK_STATE *walk_state; + + + walk_state = acpi_ds_get_current_walk_state (acpi_gbl_current_walk_list); + + + switch (type) + { + + case MTH_TYPE_LOCAL: + + if (index > MTH_MAX_LOCAL) { + return (entry); + } + + entry = &walk_state->local_variables[index]; + break; + + + case MTH_TYPE_ARG: + + if (index > MTH_MAX_ARG) { + return (entry); + } + + entry = &walk_state->arguments[index]; + break; + + + default: + break; + } + + + return (entry); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_get_value + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument to get + * *Dest_desc - Descriptor into which selected Arg + * or Local value should be copied + * + * RETURN: Status + * + * DESCRIPTION: Retrieve value of selected Arg or Local from the method frame + * at the current top of the method stack. + * Used only in Acpi_aml_resolve_to_value(). + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_get_value ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL **dest_desc) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **entry; + ACPI_OBJECT_INTERNAL *object; + + + /* Validate the object descriptor */ + + if (!dest_desc) { + return (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the requested method stack entry */ + + status = acpi_ds_method_data_get_entry (type, index, &entry); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Get the object from the method stack */ + + object = *entry; + + + /* Examine the returned object, it must be valid. */ + + if (!object) { + /* + * Index points to uninitialized object stack value. + * This means that either 1) The expected argument was + * not passed to the method, or 2) A local variable + * was referenced by the method (via the ASL) + * before it was initialized. Either case is an error. + */ + + switch (type) + { + case MTH_TYPE_ARG: + return (AE_AML_UNINITIALIZED_ARG); + break; + + case MTH_TYPE_LOCAL: + return (AE_AML_UNINITIALIZED_LOCAL); + break; + } + } + + + /* + * Index points to initialized and valid object stack value. + * Return an additional reference to the object + */ + + *dest_desc = object; + acpi_cm_add_reference (object); + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_delete_value + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument to delete + * + * RETURN: Status + * + * DESCRIPTION: Delete the entry at Type:Index on the method stack. Inserts + * a null into the stack slot after the object is deleted. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_delete_value ( + u32 type, + u32 index) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **entry; + ACPI_OBJECT_INTERNAL *object; + + + /* Get a pointer to the requested entry */ + + status = acpi_ds_method_data_get_entry (type, index, &entry); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Get the current entry in this slot k */ + + object = *entry; + + /* + * Undefine the Arg or Local by setting its descriptor + * pointer to NULL. Locals/Args can contain both + * ACPI_OBJECT_INTERNALS and ACPI_NAMED_OBJECTs + */ + *entry = NULL; + + + if ((object) && + (VALID_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_INTERNAL))) + { + /* + * There is a valid object in this slot + * Decrement the reference count by one to balance the + * increment when the object was stored in the slot. + */ + + acpi_cm_remove_reference (object); + } + + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_method_data_set_value + * + * PARAMETERS: Type - Either MTH_TYPE_LOCAL or MTH_TYPE_ARG + * Index - Which local_var or argument to set + * *Src_desc - Value to be stored + * *Dest_desc - Descriptor into which *Src_desc + * can be copied, or NULL if one must + * be allocated for the purpose. If + * provided, this descriptor will be + * used for the new value. + * + * RETURN: Status + * + * DESCRIPTION: Store a value in an Arg or Local. The Src_desc is installed + * as the new value for the Arg or Local and the reference count + * is incremented. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_method_data_set_value ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL *src_desc) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **entry; + + + /* Parameter validation */ + + if (!src_desc) { + return (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the requested method stack entry */ + + status = acpi_ds_method_data_get_entry (type, index, &entry); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + if (*entry == src_desc) { + goto cleanup; + } + + + /* + * If there is an object already in this slot, we either + * have to delete it, or if this is an argument and there + * is an object reference stored there, we have to do + * an indirect store! + */ + + if (*entry) { + /* + * Check for an indirect store if an argument + * contains an object reference (stored as an NTE). + * We don't allow this automatic dereferencing for + * locals, since a store to a local should overwrite + * anything there, including an object reference. + * + * If both Arg0 and Local0 contain Ref_of (Local4): + * + * Store (1, Arg0) - Causes indirect store to local4 + * Store (1, Local0) - Stores 1 in local0, overwriting + * the reference to local4 + * Store (1, De_refof (Local0)) - Causes indirect store to local4 + * + * Weird, but true. + */ + + if ((type == MTH_TYPE_ARG) && + (VALID_DESCRIPTOR_TYPE (*entry, ACPI_DESC_TYPE_NAMED))) + { + /* Detach an existing object from the NTE */ + + acpi_ns_detach_object (*entry); + + /* + * Store this object into the NTE + * (do the indirect store) + */ + + status = acpi_ns_attach_object (*entry, src_desc, + src_desc->common.type); + return (status); + } + + + /* + * Otherwise, just delete the existing object + * before storing the new one + */ + + acpi_ds_method_data_delete_value (type, index); + } + + + /* + * Install the Obj_stack descriptor (*Src_desc) into + * the descriptor for the Arg or Local. + * Install the new object in the stack entry + * (increments the object reference count by one) + */ + + status = acpi_ds_method_data_set_entry (type, index, src_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Normal exit */ + + return (AE_OK); + + + /* Error exit */ + +cleanup: + + return (status); +} + diff --git a/drivers/acpi/dispatcher/dsobject.c b/drivers/acpi/dispatcher/dsobject.c new file mode 100644 index 000000000..c366eb153 --- /dev/null +++ b/drivers/acpi/dispatcher/dsobject.c @@ -0,0 +1,598 @@ + +/****************************************************************************** + * + * Module Name: dsobject - Dispatcher object management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dsobject"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_init_one_object + * + * PARAMETERS: Obj_handle - NTE of the object + * Level - Current nesting level + * Context - Points to a init info struct + * Return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Callback from Acpi_walk_namespace. Invoked for every object + * within the namespace. + * + * Currently, the only objects that require initialization are: + * 1) Methods + * 2) Op Regions + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_init_one_object ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value) +{ + OBJECT_TYPE_INTERNAL type; + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + INIT_WALK_INFO *info = (INIT_WALK_INFO *) context; + + + /* + * We are only interested in objects owned by the table that + * was just loaded + */ + + if (((ACPI_NAMED_OBJECT*) obj_handle)->owner_id != + info->table_desc->table_id) + { + return AE_OK; + } + + + /* And even then, we are only interested in a few object types */ + + type = acpi_ns_get_type (obj_handle); + + switch (type) + { + + case ACPI_TYPE_REGION: + + acpi_ds_initialize_region (obj_handle); + + info->op_region_count++; + break; + + + case ACPI_TYPE_METHOD: + + info->method_count++; + + + /* + * Always parse methods to detect errors, we may delete + * the parse tree below + */ + + status = acpi_ds_parse_method (obj_handle); + + /* TBD: [Errors] what do we do with an error? */ + + if (ACPI_FAILURE (status)) { + break; + } + + /* + * Keep the parse tree only if we are parsing all methods + * at init time (versus just-in-time) + */ + + if (acpi_gbl_when_to_parse_methods != METHOD_PARSE_AT_INIT) { + + acpi_ns_delete_namespace_subtree (obj_handle); + + obj_desc = ((ACPI_NAMED_OBJECT*)obj_handle)->object; + acpi_ps_delete_parse_tree (obj_desc->method.parser_op); + obj_desc->method.parser_op = NULL; + } + + break; + + default: + break; + } + + /* + * We ignore errors from above, and always return OK, since + * we don't want to abort the walk on a single error. + */ + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_initialize_objects + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Walk the entire namespace and perform any necessary initialization + * on the objects found therein + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_initialize_objects ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAMED_OBJECT *start_entry) +{ + ACPI_STATUS status; + INIT_WALK_INFO info; + + + info.method_count = 0; + info.op_region_count = 0; + info.table_desc = table_desc; + + + /* Walk entire namespace from the supplied root */ + + status = acpi_walk_namespace (ACPI_TYPE_ANY, start_entry, + ACPI_INT32_MAX, acpi_ds_init_one_object, + &info, NULL); + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_init_object_from_op + * + * PARAMETERS: Op - Parser op used to init the internal object + * Opcode - AML opcode associated with the object + * Obj_desc - Namespace object to be initialized + * + * RETURN: Status + * + * DESCRIPTION: Initialize a namespace object from a parser Op and its + * associated arguments. The namespace object is a more compact + * representation of the Op and its arguments. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_init_object_from_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + u16 opcode, + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status; + ACPI_GENERIC_OP *arg; + ACPI_BYTELIST_OP *byte_list; + ACPI_OBJECT_INTERNAL *arg_desc; + ACPI_OP_INFO *op_info; + + + op_info = acpi_ps_get_opcode_info (opcode); + if (!op_info) { + /* Unknown opcode */ + + return AE_TYPE; + } + + + /* Get and prepare the first argument */ + + switch (obj_desc->common.type) + { + case ACPI_TYPE_BUFFER: + + /* First arg is a number */ + + acpi_ds_create_operand (walk_state, op->value.arg); + arg_desc = walk_state->operands [walk_state->num_operands - 1]; + acpi_ds_obj_stack_pop (1, walk_state); + + /* Resolve the object (could be an arg or local) */ + + status = acpi_aml_resolve_to_value (&arg_desc); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (arg_desc); + return status; + } + + /* We are expecting a number */ + + if (arg_desc->common.type != ACPI_TYPE_NUMBER) { + acpi_cm_remove_reference (arg_desc); + return AE_TYPE; + } + + /* Get the value, delete the internal object */ + + obj_desc->buffer.length = arg_desc->number.value; + acpi_cm_remove_reference (arg_desc); + + /* Allocate the buffer */ + + obj_desc->buffer.pointer = + acpi_cm_callocate (obj_desc->buffer.length); + + if (!obj_desc->buffer.pointer) { + return AE_NO_MEMORY; + } + + /* + * Second arg is the buffer data (optional) + * Byte_list can be either individual bytes or a + * string initializer! + */ + + /* skip first arg */ + arg = op->value.arg; + byte_list = (ACPI_BYTELIST_OP *) arg->next; + if (byte_list) { + if (byte_list->opcode != AML_BYTELIST_OP) { + return AE_TYPE; + } + + MEMCPY (obj_desc->buffer.pointer, byte_list->data, + obj_desc->buffer.length); + } + + break; + + + case ACPI_TYPE_NUMBER: + obj_desc->number.value = op->value.integer; + break; + + + case ACPI_TYPE_STRING: + obj_desc->string.pointer = op->value.string; + obj_desc->string.length = STRLEN (op->value.string); + break; + + + case ACPI_TYPE_METHOD: + break; + + + case INTERNAL_TYPE_REFERENCE: + + switch (op_info->flags & OP_INFO_TYPE) + { + case OPTYPE_LOCAL_VARIABLE: + + /* Split the opcode into a base opcode + offset */ + + obj_desc->reference.op_code = AML_LOCAL_OP; + obj_desc->reference.offset = opcode - AML_LOCAL_OP; + break; + + case OPTYPE_METHOD_ARGUMENT: + + /* Split the opcode into a base opcode + offset */ + + obj_desc->reference.op_code = AML_ARG_OP; + obj_desc->reference.offset = opcode - AML_ARG_OP; + break; + + default: /* Constants, Literals, etc.. */ + + if (op->opcode == AML_NAMEPATH_OP) { + /* Nte was saved in Op */ + + obj_desc->reference.nte = op->acpi_named_object; + } + + obj_desc->reference.op_code = opcode; + break; + } + + break; + + + default: + + break; + } + + return AE_OK; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_build_internal_simple_obj + * + * PARAMETERS: Op - Parser object to be translated + * Obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op object to the equivalent namespace object + * Simple objects are any objects other than a package object! + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_build_internal_simple_obj ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL **obj_desc_ptr) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + OBJECT_TYPE_INTERNAL type; + ACPI_STATUS status; + + + if (op->opcode == AML_NAMEPATH_OP) { + /* + * This is an object reference. If The name was + * previously looked up in the NS, it is stored in this op. + * Otherwise, go ahead and look it up now + */ + + if (!op->acpi_named_object) { + status = acpi_ns_lookup (walk_state->scope_info, + op->value.string, ACPI_TYPE_ANY, + IMODE_EXECUTE, + NS_SEARCH_PARENT | NS_DONT_OPEN_SCOPE, + NULL, + (ACPI_NAMED_OBJECT**)&(op->acpi_named_object)); + + if (ACPI_FAILURE (status)) { + return (status); + } + } + + /* + * The reference will be a Reference + * TBD: [Restructure] unless we really need a separate + * type of INTERNAL_TYPE_REFERENCE change + * Acpi_ds_map_opcode_to_data_type to handle this case + */ + type = INTERNAL_TYPE_REFERENCE; + } + else { + type = acpi_ds_map_opcode_to_data_type (op->opcode, NULL); + } + + + /* Create and init the internal ACPI object */ + + obj_desc = acpi_cm_create_internal_object (type); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + status = acpi_ds_init_object_from_op (walk_state, op, + op->opcode, obj_desc); + + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + return (status); + } + + *obj_desc_ptr = obj_desc; + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_build_internal_package_obj + * + * PARAMETERS: Op - Parser object to be translated + * Obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op package object to the equivalent + * namespace object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_build_internal_package_obj ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL **obj_desc_ptr) +{ + ACPI_GENERIC_OP *arg; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status = AE_OK; + + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_PACKAGE); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* The first argument must be the package length */ + + arg = op->value.arg; + obj_desc->package.count = arg->value.integer; + + /* + * Allocate the array of pointers (ptrs to the + * individual objects) Add an extra pointer slot so + * that the list is always null terminated. + */ + + obj_desc->package.elements = + acpi_cm_callocate ((obj_desc->package.count + 1) * + sizeof (void *)); + + if (!obj_desc->package.elements) { + /* Package vector allocation failure */ + + REPORT_ERROR ("Ds_build_internal_package_obj: Package vector allocation failure"); + + acpi_cm_free (obj_desc); + return (AE_NO_MEMORY); + } + + obj_desc->package.next_element = obj_desc->package.elements; + + /* + * Now init the elements of the package + */ + + arg = arg->next; + while (arg) { + if (arg->opcode == AML_PACKAGE_OP) { + status = acpi_ds_build_internal_package_obj (walk_state, arg, + obj_desc->package.next_element); + } + + else { + status = acpi_ds_build_internal_simple_obj (walk_state, arg, + obj_desc->package.next_element); + } + + obj_desc->package.next_element++; + arg = arg->next; + } + + *obj_desc_ptr = obj_desc; + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_build_internal_object + * + * PARAMETERS: Op - Parser object to be translated + * Obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op object to the equivalent namespace + * object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_build_internal_object ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL **obj_desc_ptr) +{ + ACPI_STATUS status; + + + if (op->opcode == AML_PACKAGE_OP) { + status = acpi_ds_build_internal_package_obj (walk_state, op, + obj_desc_ptr); + } + + else { + status = acpi_ds_build_internal_simple_obj (walk_state, op, + obj_desc_ptr); + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_named_object + * + * PARAMETERS: Op - Parser object to be translated + * Obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_named_object ( + ACPI_WALK_STATE *walk_state, + ACPI_NAMED_OBJECT *entry, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + + + if (!op->value.arg) { + /* No arguments, there is nothing to do */ + + return (AE_OK); + } + + + /* Build an internal object for the argument(s) */ + + status = acpi_ds_build_internal_object (walk_state, + op->value.arg, &obj_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + + /* Re-type the object according to it's argument */ + + entry->type = obj_desc->common.type; + + /* Init obj */ + + status = acpi_ns_attach_object ((ACPI_HANDLE) entry, obj_desc, + (u8) entry->type); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + return (status); + + +cleanup: + + acpi_cm_remove_reference (obj_desc); + + return (status); +} + + diff --git a/drivers/acpi/dispatcher/dsopcode.c b/drivers/acpi/dispatcher/dsopcode.c new file mode 100644 index 000000000..c2020574f --- /dev/null +++ b/drivers/acpi/dispatcher/dsopcode.c @@ -0,0 +1,464 @@ + +/****************************************************************************** + * + * Module Name: dsopcode - Dispatcher Op Region support + * and handling of "control" opcodes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "events.h" +#include "tables.h" + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dsopcode"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_get_region_arguments + * + * PARAMETERS: Rgn_desc - A valid region object + * + * RETURN: Status. + * + * DESCRIPTION: Get region address and length. This implements the late + * evaluation of these region attributes. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_get_region_arguments ( + ACPI_OBJECT_INTERNAL *rgn_desc) +{ + ACPI_OBJECT_INTERNAL *method_desc; + ACPI_NAMED_OBJECT *entry; + ACPI_GENERIC_OP *op; + ACPI_GENERIC_OP *region_op; + ACPI_STATUS status; + ACPI_TABLE_DESC *table_desc; + + + if (rgn_desc->region.region_flags & REGION_AGRUMENT_DATA_VALID) { + return (AE_OK); + } + + + method_desc = rgn_desc->region.method; + entry = rgn_desc->region.nte; + + + /* + * Allocate a new parser op to be the root of the parsed + * Op_region tree + */ + + op = acpi_ps_alloc_op (AML_REGION_OP); + if (!op) { + return AE_NO_MEMORY; + } + + /* Save the NTE for use in Acpi_ps_parse_aml */ + + op->acpi_named_object = acpi_ns_get_parent_entry (entry); + + /* Get a handle to the parent ACPI table */ + + status = acpi_tb_handle_to_object (entry->owner_id, &table_desc); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Parse the entire Op_region declaration, creating a parse tree */ + + status = acpi_ps_parse_aml (op, method_desc->method.pcode, + method_desc->method.pcode_length, 0); + if (ACPI_SUCCESS (status)) { + /* Get and init the actual Region_op created above */ + + region_op = op->value.arg; + region_op->acpi_named_object = entry; + + /* Acpi_evaluate the address and length arguments for the Op_region */ + + acpi_ps_walk_parsed_aml (region_op, region_op, NULL, NULL, NULL, + NULL, table_desc->table_id, + acpi_ds_exec_begin_op, acpi_ds_exec_end_op); + } + + /* All done with the parse tree, delete it */ + + acpi_ps_delete_parse_tree (op); + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_initialize_region + * + * PARAMETERS: Op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_initialize_region ( + ACPI_HANDLE obj_handle) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + obj_desc = acpi_ns_get_attached_object (obj_handle); + + /* Namespace is NOT locked */ + + status = acpi_ev_initialize_region (obj_desc, FALSE); + + return status; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_eval_region_operands + * + * PARAMETERS: Op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: Get region address and length + * Called from Acpi_ds_exec_end_op during Op_region parse tree walk + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_eval_region_operands ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *region_desc; + ACPI_NAMED_OBJECT *entry; + ACPI_GENERIC_OP *next_op; + + + /* + * This is where we evaluate the address and length fields of the Op_region declaration + */ + + entry = op->acpi_named_object; + + /* Next_op points to the op that holds the Space_iD */ + next_op = op->value.arg; + + /* Next_op points to address op */ + next_op = next_op->next; + + /* Acpi_evaluate/create the address and length operands */ + + status = acpi_ds_create_operands (walk_state, next_op); + if (ACPI_FAILURE (status)) { + return (status); + } + + region_desc = acpi_ns_get_attached_object (entry); + if (!region_desc) { + return (AE_NOT_EXIST); + } + + /* Get the length and save it */ + + /* Top of stack */ + obj_desc = walk_state->operands[walk_state->num_operands - 1]; + + region_desc->region.length = obj_desc->number.value; + acpi_cm_remove_reference (obj_desc); + + /* Get the address and save it */ + + /* Top of stack - 1 */ + obj_desc = walk_state->operands[walk_state->num_operands - 2]; + + region_desc->region.address = obj_desc->number.value; + acpi_cm_remove_reference (obj_desc); + + + /* Now the address and length are valid for this opregion */ + + region_desc->region.region_flags |= REGION_AGRUMENT_DATA_VALID; + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_exec_begin_control_op + * + * PARAMETERS: Walk_list - The list that owns the walk stack + * Op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_exec_begin_control_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status = AE_OK; + ACPI_GENERIC_STATE *control_state; + + + switch (op->opcode) + { + case AML_IF_OP: + case AML_WHILE_OP: + + /* + * IF/WHILE: Create a new control state to manage these + * constructs. We need to manage these as a stack, in order + * to handle nesting. + */ + + control_state = acpi_cm_create_control_state (); + if (!control_state) { + status = AE_NO_MEMORY; + break; + } + + acpi_cm_push_generic_state (&walk_state->control_state, control_state); + break; + + + case AML_ELSE_OP: + + /* Predicate is in the state object */ + /* If predicate is true, the IF was executed, ignore ELSE part */ + + if (walk_state->last_predicate) { + status = AE_CTRL_TRUE; + } + + break; + + + case AML_RETURN_OP: + + break; + + + default: + break; + } + + return status; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_exec_end_control_op + * + * PARAMETERS: Walk_list - The list that owns the walk stack + * Op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_exec_end_control_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status = AE_OK; + ACPI_GENERIC_STATE *control_state; + + + switch (op->opcode) + { + case AML_IF_OP: + + /* + * Save the result of the predicate in case there is an + * ELSE to come + */ + + walk_state->last_predicate = + (u8) walk_state->control_state->common.value; + + /* + * Pop the control state that was created at the start + * of the IF and free it + */ + + control_state = + acpi_cm_pop_generic_state (&walk_state->control_state); + + acpi_cm_delete_generic_state (control_state); + + break; + + + case AML_ELSE_OP: + + break; + + + case AML_WHILE_OP: + + if (walk_state->control_state->common.value) { + /* Predicate was true, go back and evaluate it again! */ + + status = AE_CTRL_TRUE; + } + + else { + /* Pop this control state and free it */ + + control_state = + acpi_cm_pop_generic_state (&walk_state->control_state); + acpi_cm_delete_generic_state (control_state); + } + + break; + + + case AML_RETURN_OP: + + + /* One optional operand -- the return value */ + + if (op->value.arg) { + status = acpi_ds_create_operands (walk_state, op->value.arg); + if (ACPI_FAILURE (status)) { + return status; + } + + /* + * TBD: [Restructure] Just check for NULL arg + * to signify no return value??? + */ + + /* + * If value being returned is a Reference (such as + * an arg or local), resolve it now because it may + * cease to exist at the end of the method. + */ + + status = acpi_aml_resolve_to_value (&walk_state->operands [0]); + if (ACPI_FAILURE (status)) { + return status; + } + + /* + * Get the return value and save as the last result + * value + * This is the only place where Walk_state->Return_desc + * is set to anything other than zero! + */ + + walk_state->return_desc = walk_state->operands[0]; + } + + else { + /* No return operand */ + + acpi_cm_remove_reference (walk_state->operands [0]); + + walk_state->operands [0] = NULL; + walk_state->num_operands = 0; + walk_state->return_desc = NULL; + } + + + /* End the control method execution right now */ + status = AE_CTRL_TERMINATE; + break; + + + case AML_NOOP_CODE: + + /* Just do nothing! */ + break; + + + case AML_BREAK_POINT_OP: + + /* Call up to the OS dependent layer to handle this */ + + acpi_os_breakpoint (NULL); + + /* If it returns, we are done! */ + + break; + + + case AML_BREAK_OP: + + /* + * As per the ACPI specification: + * "The break operation causes the current package + * execution to complete" + * "Break -- Stop executing the current code package + * at this point" + * + * Returning AE_FALSE here will cause termination of + * the current package, and execution will continue one + * level up, starting with the completion of the parent Op. + */ + + status = AE_CTRL_FALSE; + break; + + + default: + + status = AE_AML_BAD_OPCODE; + break; + } + + + return status; +} + diff --git a/drivers/acpi/dispatcher/dsutils.c b/drivers/acpi/dispatcher/dsutils.c new file mode 100644 index 000000000..a5f79c96e --- /dev/null +++ b/drivers/acpi/dispatcher/dsutils.c @@ -0,0 +1,694 @@ + +/****************************************************************************** + * + * Module Name: dsutils - Dispatcher utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "debugger.h" + +#define _COMPONENT PARSER + MODULE_NAME ("dsutils"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_delete_result_if_not_used + * + * PARAMETERS: Op + * Result_obj + * Walk_state + * + * RETURN: Status + * + * DESCRIPTION: Used after interpretation of an opcode. If there is an internal + * result descriptor, check if the parent opcode will actually use + * this result. If not, delete the result now so that it will + * not become orphaned. + * + ****************************************************************************/ + +void +acpi_ds_delete_result_if_not_used ( + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL *result_obj, + ACPI_WALK_STATE *walk_state) +{ + ACPI_OP_INFO *parent_info; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + if (!op) { + return; + } + + if (!result_obj) { + return; + } + + if (!op->parent) { + /* + * If there is no parent, the result can't possibly be used! + * (An executing method typically has no parent, since each + * method is parsed separately + */ + + /* + * Must pop the result stack (Obj_desc should be equal + * to Result_obj) + */ + + status = acpi_ds_result_stack_pop (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return; + } + + acpi_cm_remove_reference (result_obj); + + return; + } + + + /* + * Get info on the parent. The root Op is AML_SCOPE + */ + + parent_info = acpi_ps_get_opcode_info (op->parent->opcode); + if (!parent_info) { + return; + } + + + /* Never delete the return value associated with a return opcode */ + + if (op->parent->opcode == AML_RETURN_OP) { + return; + } + + + /* + * Decide what to do with the result based on the parent. If + * the parent opcode will not use the result, delete the object. + * Otherwise leave it as is, it will be deleted when it is used + * as an operand later. + */ + + switch (parent_info->flags & OP_INFO_TYPE) + { + /* + * In these cases, the parent will never use the return object, + * so delete it here and now. + */ + case OPTYPE_CONTROL: /* IF, ELSE, WHILE only */ + case OPTYPE_NAMED_OBJECT: /* Scope, method, etc. */ + + /* + * Must pop the result stack (Obj_desc should be equal + * to Result_obj) + */ + + status = acpi_ds_result_stack_pop (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return; + } + + acpi_cm_remove_reference (result_obj); + break; + + /* + * In all other cases. the parent will actually use the return + * object, so keep it. + */ + default: + break; + } + + return; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_operand + * + * PARAMETERS: Walk_state + * Arg + * + * RETURN: Status + * + * DESCRIPTION: Translate a parse tree object that is an argument to an AML + * opcode to the equivalent interpreter object. This may include + * looking up a name or entering a new name into the internal + * namespace. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_operand ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *arg) +{ + ACPI_STATUS status = AE_OK; + char *name_string; + u32 name_length; + OBJECT_TYPE_INTERNAL data_type; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_GENERIC_OP *parent_op; + u16 opcode; + u32 flags; + OPERATING_MODE interpreter_mode; + + + /* A valid name must be looked up in the namespace */ + + if ((arg->opcode == AML_NAMEPATH_OP) && + (arg->value.string)) + { + /* Get the entire name string from the AML stream */ + + status = acpi_aml_get_name_string (ACPI_TYPE_ANY, + arg->value.buffer, + &name_string, + &name_length); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * All prefixes have been handled, and the name is + * in Name_string + */ + + /* + * Differentiate between a namespace "create" operation + * versus a "lookup" operation (IMODE_LOAD_PASS2 vs. + * IMODE_EXECUTE) in order to support the creation of + * namespace objects during the execution of control methods. + */ + + parent_op = arg->parent; + if ((acpi_ps_is_named_object_op (parent_op->opcode)) && + (parent_op->opcode != AML_METHODCALL_OP) && + (parent_op->opcode != AML_NAMEPATH_OP)) + { + /* Enter name into namespace if not found */ + + interpreter_mode = IMODE_LOAD_PASS2; + } + + else { + /* Return a failure if name not found */ + + interpreter_mode = IMODE_EXECUTE; + } + + status = acpi_ns_lookup (walk_state->scope_info, name_string, + ACPI_TYPE_ANY, interpreter_mode, + NS_SEARCH_PARENT | NS_DONT_OPEN_SCOPE, + walk_state, + (ACPI_NAMED_OBJECT**) &obj_desc); + + /* Free the namestring created above */ + + acpi_cm_free (name_string); + + /* + * The only case where we pass through (ignore) a NOT_FOUND + * error is for the Cond_ref_of opcode. + */ + + if (status == AE_NOT_FOUND) { + if (parent_op->opcode == AML_COND_REF_OF_OP) { + /* + * For the Conditional Reference op, it's OK if + * the name is not found; We just need a way to + * indicate this to the interpreter, set the + * object to the root + */ + obj_desc = (ACPI_OBJECT_INTERNAL *) acpi_gbl_root_object; + status = AE_OK; + } + + else { + /* + * We just plain didn't find it -- which is a + * very serious error at this point + */ + status = AE_AML_NAME_NOT_FOUND; + } + } + + /* Check status from the lookup */ + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Put the resulting object onto the current object stack */ + + status = acpi_ds_obj_stack_push (obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + + else { + /* Check for null name case */ + + if (arg->opcode == AML_NAMEPATH_OP) { + /* + * If the name is null, this means that this is an + * optional result parameter that was not specified + * in the original ASL. Create an Reference for a + * placeholder + */ + opcode = AML_ZERO_OP; /* Has no arguments! */ + + /* + * TBD: [Investigate] anything else needed for the + * zero op lvalue? + */ + } + + else { + opcode = arg->opcode; + } + + + /* Get the data type of the argument */ + + data_type = acpi_ds_map_opcode_to_data_type (opcode, &flags); + if (data_type == INTERNAL_TYPE_INVALID) { + return (AE_NOT_IMPLEMENTED); + } + + if (flags & OP_HAS_RETURN_VALUE) { + /* + * Use value that was already previously returned + * by the evaluation of this argument + */ + + status = acpi_ds_result_stack_pop (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + /* + * Only error is underflow, and this indicates + * a missing or null operand! + */ + return (status); + } + + } + + else { + /* Create an ACPI_INTERNAL_OBJECT for the argument */ + + obj_desc = acpi_cm_create_internal_object (data_type); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* Initialize the new object */ + + status = acpi_ds_init_object_from_op (walk_state, arg, + opcode, obj_desc); + if (ACPI_FAILURE (status)) { + acpi_cm_free (obj_desc); + return (status); + } + } + + /* Put the operand object on the object stack */ + + status = acpi_ds_obj_stack_push (obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + } + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_create_operands + * + * PARAMETERS: First_arg - First argument of a parser argument tree + * + * RETURN: Status + * + * DESCRIPTION: Convert an operator's arguments from a parse tree format to + * namespace objects and place those argument object on the object + * stack in preparation for evaluation by the interpreter. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_create_operands ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *first_arg) +{ + ACPI_STATUS status = AE_OK; + ACPI_GENERIC_OP *arg; + u32 args_pushed = 0; + + + arg = first_arg; + + + /* For all arguments in the list... */ + + while (arg) { + + status = acpi_ds_create_operand (walk_state, arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Move on to next argument, if any */ + + arg = arg->next; + args_pushed++; + } + + return (status); + + +cleanup: + /* + * We must undo everything done above; meaning that we must + * pop everything off of the operand stack and delete those + * objects + */ + + acpi_ds_obj_stack_pop_and_delete (args_pushed, walk_state); + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_resolve_operands + * + * PARAMETERS: Walk_state - Current walk state with operands on stack + * + * RETURN: Status + * + * DESCRIPTION: Resolve all operands to their values. Used to prepare + * arguments to a control method invocation (a call from one + * method to another.) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_resolve_operands ( + ACPI_WALK_STATE *walk_state) +{ + u32 i; + ACPI_STATUS status = AE_OK; + + + /* + * Attempt to resolve each of the valid operands + * Method arguments are passed by value, not by reference + */ + + /* + * TBD: [Investigate] Note from previous parser: + * Ref_of problem with Acpi_aml_resolve_to_value() conversion. + */ + + for (i = 0; i < walk_state->num_operands; i++) { + status = acpi_aml_resolve_to_value (&walk_state->operands[i]); + if (ACPI_FAILURE (status)) { + break; + } + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_map_opcode_to_data_type + * + * PARAMETERS: Opcode - AML opcode to map + * Out_flags - Additional info about the opcode + * + * RETURN: The ACPI type associated with the opcode + * + * DESCRIPTION: Convert a raw AML opcode to the associated ACPI data type, + * if any. If the opcode returns a value as part of the + * intepreter execution, a flag is returned in Out_flags. + * + ****************************************************************************/ + +OBJECT_TYPE_INTERNAL +acpi_ds_map_opcode_to_data_type ( + u16 opcode, + u32 *out_flags) +{ + OBJECT_TYPE_INTERNAL data_type = INTERNAL_TYPE_INVALID; + ACPI_OP_INFO *op_info; + u32 flags = 0; + + + op_info = acpi_ps_get_opcode_info (opcode); + if (!op_info) { + /* Unknown opcode */ + + return data_type; + } + + switch (op_info->flags & OP_INFO_TYPE) + { + + case OPTYPE_LITERAL: + + switch (opcode) + { + case AML_BYTE_OP: + case AML_WORD_OP: + case AML_DWORD_OP: + + data_type = ACPI_TYPE_NUMBER; + break; + + + case AML_STRING_OP: + + data_type = ACPI_TYPE_STRING; + break; + + case AML_NAMEPATH_OP: + data_type = INTERNAL_TYPE_REFERENCE; + break; + } + + break; + + + case OPTYPE_DATA_TERM: + + switch (opcode) + { + case AML_BUFFER_OP: + + data_type = ACPI_TYPE_BUFFER; + break; + + case AML_PACKAGE_OP: + + data_type = ACPI_TYPE_PACKAGE; + break; + } + + break; + + + case OPTYPE_CONSTANT: + case OPTYPE_METHOD_ARGUMENT: + case OPTYPE_LOCAL_VARIABLE: + + data_type = INTERNAL_TYPE_REFERENCE; + break; + + + case OPTYPE_MONADIC2: + case OPTYPE_MONADIC2_r: + case OPTYPE_DYADIC2: + case OPTYPE_DYADIC2_r: + case OPTYPE_DYADIC2_s: + case OPTYPE_INDEX: + case OPTYPE_MATCH: + + flags = OP_HAS_RETURN_VALUE; + data_type = ACPI_TYPE_ANY; + + break; + + case OPTYPE_METHOD_CALL: + + flags = OP_HAS_RETURN_VALUE; + data_type = ACPI_TYPE_METHOD; + + break; + + + case OPTYPE_NAMED_OBJECT: + + data_type = acpi_ds_map_named_opcode_to_data_type (opcode); + + break; + + + case OPTYPE_DYADIC1: + case OPTYPE_CONTROL: + + /* No mapping needed at this time */ + + break; + + + default: + + break; + } + + /* Return flags to caller if requested */ + + if (out_flags) { + *out_flags = flags; + } + + return data_type; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_map_named_opcode_to_data_type + * + * PARAMETERS: Opcode - The Named AML opcode to map + * + * RETURN: The ACPI type associated with the named opcode + * + * DESCRIPTION: Convert a raw Named AML opcode to the associated data type. + * Named opcodes are a subsystem of the AML opcodes. + * + ****************************************************************************/ + +OBJECT_TYPE_INTERNAL +acpi_ds_map_named_opcode_to_data_type ( + u16 opcode) +{ + OBJECT_TYPE_INTERNAL data_type; + + + /* Decode Opcode */ + + switch (opcode) + { + case AML_SCOPE_OP: + data_type = INTERNAL_TYPE_SCOPE; + break; + + case AML_DEVICE_OP: + data_type = ACPI_TYPE_DEVICE; + break; + + case AML_THERMAL_ZONE_OP: + data_type = ACPI_TYPE_THERMAL; + break; + + case AML_METHOD_OP: + data_type = ACPI_TYPE_METHOD; + break; + + case AML_POWER_RES_OP: + data_type = ACPI_TYPE_POWER; + break; + + case AML_PROCESSOR_OP: + data_type = ACPI_TYPE_PROCESSOR; + break; + + case AML_DEF_FIELD_OP: /* Def_field_op */ + data_type = INTERNAL_TYPE_DEF_FIELD_DEFN; + break; + + case AML_INDEX_FIELD_OP: /* Index_field_op */ + data_type = INTERNAL_TYPE_INDEX_FIELD_DEFN; + break; + + case AML_BANK_FIELD_OP: /* Bank_field_op */ + data_type = INTERNAL_TYPE_BANK_FIELD_DEFN; + break; + + case AML_NAMEDFIELD_OP: /* NO CASE IN ORIGINAL */ + data_type = ACPI_TYPE_ANY; + break; + + case AML_NAME_OP: /* Name_op - special code in original */ + case AML_NAMEPATH_OP: + data_type = ACPI_TYPE_ANY; + break; + + case AML_ALIAS_OP: + data_type = INTERNAL_TYPE_ALIAS; + break; + + case AML_MUTEX_OP: + data_type = ACPI_TYPE_MUTEX; + break; + + case AML_EVENT_OP: + data_type = ACPI_TYPE_EVENT; + break; + + case AML_REGION_OP: + data_type = ACPI_TYPE_REGION; + break; + + + default: + data_type = ACPI_TYPE_ANY; + break; + + } + + return data_type; +} + + diff --git a/drivers/acpi/dispatcher/dswexec.c b/drivers/acpi/dispatcher/dswexec.c new file mode 100644 index 000000000..707b30031 --- /dev/null +++ b/drivers/acpi/dispatcher/dswexec.c @@ -0,0 +1,572 @@ +/****************************************************************************** + * + * Module Name: dswexec - Dispatcher method execution callbacks; + * Dispatch to interpreter. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "debugger.h" + + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dswexec"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_exec_begin_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been reached in the + * walk; Arguments have not been evaluated yet. + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the execution of control + * methods. This is where most operators and operands are + * dispatched to the interpreter. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_exec_begin_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_OP_INFO *op_info; + ACPI_STATUS status = AE_OK; + + + if (op == walk_state->origin) { + return (AE_OK); + } + + /* + * If the previous opcode was a conditional, this opcode + * must be the beginning of the associated predicate. + * Save this knowledge in the current scope descriptor + */ + + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + CONTROL_CONDITIONAL_EXECUTING)) + { + walk_state->control_state->common.state = CONTROL_PREDICATE_EXECUTING; + + /* Save start of predicate */ + + walk_state->control_state->control.predicate_op = op; + } + + + op_info = acpi_ps_get_opcode_info (op->opcode); + + /* We want to send namepaths to the load code */ + + if (op->opcode == AML_NAMEPATH_OP) { + op_info->flags = OPTYPE_NAMED_OBJECT; + } + + + /* + * Handle the opcode based upon the opcode type + */ + + switch (op_info->flags & OP_INFO_TYPE) + { + case OPTYPE_CONTROL: + + status = acpi_ds_exec_begin_control_op (walk_state, op); + break; + + + case OPTYPE_NAMED_OBJECT: + + if (walk_state->origin->opcode == AML_METHOD_OP) { + /* + * Found a named object declaration during method + * execution; we must enter this object into the + * namespace. The created object is temporary and + * will be deleted upon completion of the execution + * of this method. + */ + + status = acpi_ds_load2_begin_op (walk_state, op); + } + break; + + + default: + break; + } + + /* Nothing to do here during method execution */ + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_exec_end_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the execution of control + * methods. The only thing we really need to do here is to + * notice the beginning of IF, ELSE, and WHILE blocks. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_exec_end_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status = AE_OK; + u16 opcode; + u8 optype; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_GENERIC_OP *next_op; + ACPI_NAMED_OBJECT *entry; + ACPI_GENERIC_OP *first_arg; + ACPI_OBJECT_INTERNAL *result_obj = NULL; + ACPI_OP_INFO *op_info; + u32 operand_index; + + + opcode = (u16) op->opcode; + + + op_info = acpi_ps_get_opcode_info (op->opcode); + if (!op_info) { + return (AE_NOT_IMPLEMENTED); + } + + optype = (u8) (op_info->flags & OP_INFO_TYPE); + first_arg = op->value.arg; + + /* Init the walk state */ + + walk_state->num_operands = 0; + walk_state->return_desc = NULL; + + + /* Call debugger for single step support (DEBUG build only) */ + + + /* Decode the opcode */ + + switch (optype) + { + case OPTYPE_UNDEFINED: + + return (AE_NOT_IMPLEMENTED); + break; + + + case OPTYPE_BOGUS: + break; + + case OPTYPE_CONSTANT: /* argument type only */ + case OPTYPE_LITERAL: /* argument type only */ + case OPTYPE_DATA_TERM: /* argument type only */ + case OPTYPE_LOCAL_VARIABLE: /* argument type only */ + case OPTYPE_METHOD_ARGUMENT: /* argument type only */ + break; + + + /* most operators with arguments */ + + case OPTYPE_MONADIC1: + case OPTYPE_DYADIC1: + case OPTYPE_MONADIC2: + case OPTYPE_MONADIC2_r: + case OPTYPE_DYADIC2: + case OPTYPE_DYADIC2_r: + case OPTYPE_DYADIC2_s: + case OPTYPE_RECONFIGURATION: + case OPTYPE_INDEX: + case OPTYPE_MATCH: + case OPTYPE_CREATE_FIELD: + case OPTYPE_FATAL: + + status = acpi_ds_create_operands (walk_state, first_arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + operand_index = walk_state->num_operands - 1; + + switch (optype) + { + + case OPTYPE_MONADIC1: + + /* 1 Operand, 0 External_result, 0 Internal_result */ + + status = acpi_aml_exec_monadic1 (opcode, walk_state); + break; + + + case OPTYPE_MONADIC2: + + /* 1 Operand, 0 External_result, 1 Internal_result */ + + status = acpi_aml_exec_monadic2 (opcode, walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_MONADIC2_r: + + /* 1 Operand, 1 External_result, 1 Internal_result */ + + status = acpi_aml_exec_monadic2_r (opcode, walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_DYADIC1: + + /* 2 Operands, 0 External_result, 0 Internal_result */ + + status = acpi_aml_exec_dyadic1 (opcode, walk_state); + + break; + + + case OPTYPE_DYADIC2: + + /* 2 Operands, 0 External_result, 1 Internal_result */ + + status = acpi_aml_exec_dyadic2 (opcode, walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_DYADIC2_r: + + /* 2 Operands, 1 or 2 External_results, 1 Internal_result */ + + + /* NEW INTERFACE: + * Pass in Walk_state, keep result obj but let interpreter + * push the result + */ + + status = acpi_aml_exec_dyadic2_r (opcode, walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_DYADIC2_s: /* Synchronization Operator */ + + /* 2 Operands, 0 External_result, 1 Internal_result */ + + status = acpi_aml_exec_dyadic2_s (opcode, walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_RECONFIGURATION: + + /* 1 or 2 operands, 0 Internal Result */ + + status = acpi_aml_exec_reconfiguration (opcode, walk_state); + break; + + + case OPTYPE_CREATE_FIELD: + + /* 3 or 4 Operands, 0 External_result, 0 Internal_result */ + + status = acpi_aml_exec_create_field (opcode, walk_state); + break; + + + case OPTYPE_FATAL: + + /* 3 Operands, 0 External_result, 0 Internal_result */ + + status = acpi_aml_exec_fatal (walk_state); + break; + + + case OPTYPE_INDEX: /* Type 2 opcode with 3 operands */ + + /* 3 Operands, 1 External_result, 1 Internal_result */ + + status = acpi_aml_exec_index (walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + + + case OPTYPE_MATCH: /* Type 2 opcode with 6 operands */ + + /* 6 Operands, 0 External_result, 1 Internal_result */ + + status = acpi_aml_exec_match (walk_state, &result_obj); + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_push (result_obj, walk_state); + } + + break; + } + + break; + + + case OPTYPE_CONTROL: /* Type 1 opcode, IF/ELSE/WHILE/NOOP */ + + /* 1 Operand, 0 External_result, 0 Internal_result */ + + status = acpi_ds_exec_end_control_op (walk_state, op); + + break; + + + case OPTYPE_METHOD_CALL: + + /* + * (AML_METHODCALL) Op->Value->Arg->Acpi_named_object contains + * the method NTE pointer + */ + /* Next_op points to the op that holds the method name */ + next_op = first_arg; + entry = next_op->acpi_named_object; + + /* Next_op points to first argument op */ + next_op = next_op->next; + + + /* + * Get the method's arguments and put them on the operand stack + */ + + status = acpi_ds_create_operands (walk_state, next_op); + if (ACPI_FAILURE (status)) { + break; + } + + /* + * Since the operands will be passed to another + * control method, we must resolve all local + * references here (Local variables, arguments + * to *this* method, etc.) + */ + + status = acpi_ds_resolve_operands (walk_state); + if (ACPI_FAILURE (status)) { + break; + } + + /* Open new scope on the scope stack */ +/* + Status = Acpi_ns_scope_stack_push_entry (Entry); + if (ACPI_FAILURE (Status)) { + break; + } +*/ + + /* Tell the walk loop to preempt this running method and + execute the new method */ + + status = AE_CTRL_PENDING; + + /* Return now; we don't want to disturb anything, + especially the operand count! */ + + return (status); + break; + + + case OPTYPE_NAMED_OBJECT: + + + if ((walk_state->origin->opcode == AML_METHOD_OP) && + (walk_state->origin != op)) + { + status = acpi_ds_load2_end_op (walk_state, op); + if (ACPI_FAILURE (status)) { + break; + } + } + + switch (op->opcode) + { + case AML_REGION_OP: + + status = acpi_ds_eval_region_operands (walk_state, op); + + break; + + + case AML_METHOD_OP: + + break; + + + case AML_ALIAS_OP: + + /* Alias creation was already handled by call + to psxload above */ + break; + + + default: + /* Nothing needs to be done */ + + status = AE_OK; + break; + } + + break; + + default: + + status = AE_NOT_IMPLEMENTED; + break; + } + + + /* + * Check if we just completed the evaluation of a + * conditional predicate + */ + + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + CONTROL_PREDICATE_EXECUTING) && + (walk_state->control_state->control.predicate_op == op)) + { + /* Completed the predicate, the result must be a number */ + + walk_state->control_state->common.state = 0; + + if (result_obj) { + status = acpi_ds_result_stack_pop (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + } + + else { + status = acpi_ds_create_operand (walk_state, op); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_aml_resolve_to_value (&walk_state->operands [0]); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + obj_desc = walk_state->operands [0]; + } + + if (!obj_desc) { + status = AE_AML_NO_OPERAND; + goto cleanup; + } + + if (obj_desc->common.type != ACPI_TYPE_NUMBER) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + /* Save the result of the predicate evaluation on + the control stack */ + + if (obj_desc->number.value) { + walk_state->control_state->common.value = TRUE; + } + else { + /* Predicate is FALSE, we will just toss the + rest of the package */ + + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_FALSE; + } + + /* Break to debugger to display result */ + + /* Delete the predicate result object (we know that + we don't need it anymore) and cleanup the stack */ + + acpi_cm_remove_reference (obj_desc); + result_obj = NULL; + + walk_state->control_state->common.state = CONTROL_NORMAL; + } + + +cleanup: + + if (result_obj) { + /* Break to debugger to display result */ + + /* + * Delete the result op if and only if: + * Parent will not use the result -- such as any + * non-nested type2 op in a method (parent will be method) + */ + acpi_ds_delete_result_if_not_used (op, result_obj, walk_state); + } + + /* Always clear the object stack */ + + /* TBD: [Investigate] Clear stack of return value, + but don't delete it */ + walk_state->num_operands = 0; + + return (status); +} + + diff --git a/drivers/acpi/dispatcher/dswload.c b/drivers/acpi/dispatcher/dswload.c new file mode 100644 index 000000000..bfb54593f --- /dev/null +++ b/drivers/acpi/dispatcher/dswload.c @@ -0,0 +1,600 @@ + +/****************************************************************************** + * + * Module Name: dswload - Dispatcher namespace load callbacks + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "events.h" + + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dswload"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_load1_begin_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been reached in the + * walk; Arguments have not been evaluated yet. + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_load1_begin_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_NAMED_OBJECT *new_entry; + ACPI_STATUS status; + OBJECT_TYPE_INTERNAL data_type; + + + /* We are only interested in opcodes that have an associated name */ + + if (!acpi_ps_is_named_op (op->opcode)) { + return AE_OK; + } + + + /* Check if this object has already been installed in the namespace */ + + if (op->acpi_named_object) { + return AE_OK; + } + + /* Map the raw opcode into an internal object type */ + + data_type = acpi_ds_map_named_opcode_to_data_type (op->opcode); + + /* Attempt to type a NAME opcode by examining the argument */ + + /* TBD: [Investigate] is this the right place to do this? */ + + if (op->opcode == AML_NAME_OP) { + if (op->value.arg) { + + data_type = acpi_ds_map_opcode_to_data_type ((op->value.arg)->opcode, + NULL); + } + } + + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that involve + * arguments to the opcode must be created as we go back up the parse tree later. + */ + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &((ACPI_NAMED_OP *)op)->name, + data_type, IMODE_LOAD_PASS1, + NS_NO_UPSEARCH, walk_state, &(new_entry)); + + if (ACPI_SUCCESS (status)) { + /* + * Put the NTE in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->acpi_named_object = new_entry; + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_load1_end_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_load1_end_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + OBJECT_TYPE_INTERNAL data_type; + + + /* We are only interested in opcodes that have an associated name */ + + if (!acpi_ps_is_named_op (op->opcode)) { + return AE_OK; + } + + /* TBD: [Investigate] can this be removed? */ + + if (op->opcode == AML_SCOPE_OP) { + if (((ACPI_NAMED_OP *)op)->name == -1) { + return AE_OK; + } + } + + + data_type = acpi_ds_map_named_opcode_to_data_type (op->opcode); + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope (data_type)) { + + acpi_ds_scope_stack_pop (walk_state); + } + + return AE_OK; + +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_load2_begin_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been reached in the + * walk; Arguments have not been evaluated yet. + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_load2_begin_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_NAMED_OBJECT *new_entry; + ACPI_STATUS status; + OBJECT_TYPE_INTERNAL data_type; + char *buffer_ptr; + void *original = NULL; + + + /* We only care about Namespace opcodes here */ + + if (!acpi_ps_is_namespace_op (op->opcode) && + op->opcode != AML_NAMEPATH_OP) + { + return AE_OK; + } + + + /* + * Get the name we are going to enter or lookup in the namespace + */ + if (op->opcode == AML_NAMEPATH_OP) { + /* For Namepath op , get the path string */ + + buffer_ptr = op->value.string; + if (!buffer_ptr) { + /* No name, just exit */ + + return AE_OK; + } + } + + else { + /* Get name from the op */ + + buffer_ptr = (char *) &((ACPI_NAMED_OP *)op)->name; + } + + + /* Map the raw opcode into an internal object type */ + + data_type = acpi_ds_map_named_opcode_to_data_type (op->opcode); + + + if (op->opcode == AML_DEF_FIELD_OP || + op->opcode == AML_BANK_FIELD_OP || + op->opcode == AML_INDEX_FIELD_OP) + { + new_entry = NULL; + status = AE_OK; + } + + else if (op->opcode == AML_NAMEPATH_OP) { + /* + * The Name_path is an object reference to an existing object. Don't enter the + * name into the namespace, but look it up for use later + */ + status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, + data_type, IMODE_EXECUTE, + NS_SEARCH_PARENT, walk_state, + &(new_entry)); + } + + else { + if (op->acpi_named_object) { + original = op->acpi_named_object; + new_entry = op->acpi_named_object; + + if (acpi_ns_opens_scope (data_type)) { + status = acpi_ds_scope_stack_push (new_entry->child_table, + data_type, + walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + } + return AE_OK; + } + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that involve + * arguments to the opcode must be created as we go back up the parse tree later. + */ + status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, + data_type, IMODE_EXECUTE, + NS_NO_UPSEARCH, walk_state, + &(new_entry)); + } + + if (ACPI_SUCCESS (status)) { + /* + * Put the NTE in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->acpi_named_object = new_entry; + + } + + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ds_load2_end_op + * + * PARAMETERS: Walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ds_load2_end_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op) +{ + ACPI_STATUS status = AE_OK; + OBJECT_TYPE_INTERNAL data_type; + ACPI_NAMED_OBJECT *entry; + ACPI_GENERIC_OP *arg; + ACPI_NAMED_OBJECT *new_entry; + + + if (!acpi_ps_is_namespace_object_op (op->opcode)) { + return AE_OK; + } + + if (op->opcode == AML_SCOPE_OP) { + if (((ACPI_NAMED_OP *)op)->name == -1) { + return AE_OK; + } + } + + + data_type = acpi_ds_map_named_opcode_to_data_type (op->opcode); + + /* + * Get the NTE/name from the earlier lookup + * (It was saved in the *op structure) + */ + entry = op->acpi_named_object; + + /* + * Put the NTE on the object stack (Contains the ACPI Name of + * this object) + */ + + walk_state->operands[0] = (void *) entry; + walk_state->num_operands = 1; + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope (data_type)) { + + acpi_ds_scope_stack_pop (walk_state); + } + + + /* + * Named operations are as follows: + * + * AML_SCOPE + * AML_DEVICE + * AML_THERMALZONE + * AML_METHOD + * AML_POWERRES + * AML_PROCESSOR + * AML_FIELD + * AML_INDEXFIELD + * AML_BANKFIELD + * AML_NAMEDFIELD + * AML_NAME + * AML_ALIAS + * AML_MUTEX + * AML_EVENT + * AML_OPREGION + * AML_CREATEFIELD + * AML_CREATEBITFIELD + * AML_CREATEBYTEFIELD + * AML_CREATEWORDFIELD + * AML_CREATEDWORDFIELD + * AML_METHODCALL + */ + + + /* Decode the opcode */ + + arg = op->value.arg; + + switch (op->opcode) + { + + case AML_CREATE_FIELD_OP: + case AML_BIT_FIELD_OP: + case AML_BYTE_FIELD_OP: + case AML_WORD_FIELD_OP: + case AML_DWORD_FIELD_OP: + + /* Get the Name_string argument */ + + if (op->opcode == AML_CREATE_FIELD_OP) { + arg = acpi_ps_get_arg (op, 3); + } + else { + /* Create Bit/Byte/Word/Dword field */ + + arg = acpi_ps_get_arg (op, 2); + } + + /* + * Enter the Name_string into the namespace + */ + + status = acpi_ns_lookup (walk_state->scope_info, + arg->value.string, + INTERNAL_TYPE_DEF_ANY, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + walk_state, &(new_entry)); + + if (ACPI_SUCCESS (status)) { + /* We could put the returned object (NTE) on the object stack for later, but + * for now, we will put it in the "op" object that the parser uses, so we + * can get it again at the end of this scope + */ + op->acpi_named_object = new_entry; + } + + break; + + + case AML_METHODCALL_OP: + + /* + * Lookup the method name and save the NTE + */ + + status = acpi_ns_lookup (walk_state->scope_info, arg->value.string, + ACPI_TYPE_ANY, IMODE_LOAD_PASS2, + NS_SEARCH_PARENT | NS_DONT_OPEN_SCOPE, + walk_state, &(new_entry)); + + if (ACPI_SUCCESS (status)) { + +/* has name already been resolved by here ??*/ + + /* TBD: [Restructure] Make sure that what we found is indeed a method! */ + /* We didn't search for a method on purpose, to see if the name would resolve! */ + + /* We could put the returned object (NTE) on the object stack for later, but + * for now, we will put it in the "op" object that the parser uses, so we + * can get it again at the end of this scope + */ + op->acpi_named_object = new_entry; + } + + + break; + + + case AML_PROCESSOR_OP: + + /* Nothing to do other than enter object into namespace */ + + status = acpi_aml_exec_create_processor (op, (ACPI_HANDLE) entry); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + break; + + + case AML_POWER_RES_OP: + + /* Nothing to do other than enter object into namespace */ + + status = acpi_aml_exec_create_power_resource (op, (ACPI_HANDLE) entry); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + break; + + + case AML_THERMAL_ZONE_OP: + + /* Nothing to do other than enter object into namespace */ + + break; + + + case AML_DEF_FIELD_OP: + + arg = op->value.arg; + + status = acpi_ds_create_field (op, + (ACPI_HANDLE) arg->acpi_named_object, + walk_state); + break; + + + case AML_INDEX_FIELD_OP: + + arg = op->value.arg; + + status = acpi_ds_create_index_field (op, + (ACPI_HANDLE) arg->acpi_named_object, + walk_state); + break; + + + case AML_BANK_FIELD_OP: + + arg = op->value.arg; + status = acpi_ds_create_bank_field (op, + (ACPI_HANDLE) arg->acpi_named_object, + walk_state); + break; + + + /* + * Method_op Pkg_length Names_string Method_flags Term_list + */ + case AML_METHOD_OP: + + if (!entry->object) { + status = acpi_aml_exec_create_method (((ACPI_DEFERRED_OP *) op)->body, + ((ACPI_DEFERRED_OP *) op)->body_length, + arg->value.integer, (ACPI_HANDLE) entry); + } + + break; + + + case AML_MUTEX_OP: + + status = acpi_ds_create_operands (walk_state, arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_aml_exec_create_mutex (walk_state); + break; + + + case AML_EVENT_OP: + + status = acpi_ds_create_operands (walk_state, arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_aml_exec_create_event (walk_state); + break; + + + case AML_REGION_OP: + + + /* + * The Op_region is not fully parsed at this time. Only valid argument is the Space_id. + * (We must save the address of the AML of the address and length operands) + */ + + status = acpi_aml_exec_create_region (((ACPI_DEFERRED_OP *) op)->body, + ((ACPI_DEFERRED_OP *) op)->body_length, + arg->value.integer, walk_state); + + break; + + + /* Namespace Modifier Opcodes */ + + case AML_ALIAS_OP: + + status = acpi_ds_create_operands (walk_state, arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_aml_exec_create_alias (walk_state); + break; + + + case AML_NAME_OP: + + status = acpi_ds_create_named_object (walk_state, entry, op); + + break; + + + case AML_NAMEPATH_OP: + + break; + + + default: + break; + } + + +cleanup: + /* Remove the NTE pushed at the very beginning */ + acpi_ds_obj_stack_pop (1, walk_state); + return (status); +} + diff --git a/drivers/acpi/dispatcher/dswscope.c b/drivers/acpi/dispatcher/dswscope.c new file mode 100644 index 000000000..9deb499f2 --- /dev/null +++ b/drivers/acpi/dispatcher/dswscope.c @@ -0,0 +1,161 @@ + +/****************************************************************************** + * + * Module Name: dswscope - Scope stack manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "dispatch.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("dswscope"); + + +#define STACK_POP(head) head + + +/**************************************************************************** + * + * FUNCTION: Acpi_ds_scope_stack_clear + * + * PARAMETERS: None + * + * DESCRIPTION: Pop (and free) everything on the scope stack except the + * root scope object (which remains at the stack top.) + * + ***************************************************************************/ + +void +acpi_ds_scope_stack_clear ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_GENERIC_STATE *scope_info; + + + while (walk_state->scope_info) { + /* Pop a scope off the stack */ + + scope_info = walk_state->scope_info; + walk_state->scope_info = scope_info->scope.next; + + acpi_cm_delete_generic_state (scope_info); + } +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ds_scope_stack_push + * + * PARAMETERS: *New_scope, - Name to be made current + * Type, - Type of frame being pushed + * + * DESCRIPTION: Push the current scope on the scope stack, and make the + * passed nte current. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ds_scope_stack_push ( + ACPI_NAME_TABLE *new_scope, + OBJECT_TYPE_INTERNAL type, + ACPI_WALK_STATE *walk_state) +{ + ACPI_GENERIC_STATE *scope_info; + + + if (!new_scope) { + /* invalid scope */ + + REPORT_ERROR ("Ds_scope_stack_push: null scope passed"); + return (AE_BAD_PARAMETER); + } + + /* Make sure object type is valid */ + + if (!acpi_aml_validate_object_type (type)) { + REPORT_WARNING ("Ds_scope_stack_push: type code out of range"); + } + + + /* Allocate a new scope object */ + + scope_info = acpi_cm_create_generic_state (); + if (!scope_info) { + return (AE_NO_MEMORY); + } + + /* Init new scope object */ + + scope_info->scope.name_table = new_scope; + scope_info->common.value = (u16) type; + + /* Push new scope object onto stack */ + + acpi_cm_push_generic_state (&walk_state->scope_info, scope_info); + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ds_scope_stack_pop + * + * PARAMETERS: Type - The type of frame to be found + * + * DESCRIPTION: Pop the scope stack until a frame of the requested type + * is found. + * + * RETURN: Count of frames popped. If no frame of the requested type + * was found, the count is returned as a negative number and + * the scope stack is emptied (which sets the current scope + * to the root). If the scope stack was empty at entry, the + * function is a no-op and returns 0. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ds_scope_stack_pop ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_GENERIC_STATE *scope_info; + + + /* + * Pop scope info object off the stack. + */ + + scope_info = acpi_cm_pop_generic_state (&walk_state->scope_info); + if (!scope_info) { + return (AE_STACK_UNDERFLOW); + } + + acpi_cm_delete_generic_state (scope_info); + + return (AE_OK); +} + + diff --git a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/dispatcher/dswstate.c new file mode 100644 index 000000000..e98f4e36c --- /dev/null +++ b/drivers/acpi/dispatcher/dswstate.c @@ -0,0 +1,648 @@ +/****************************************************************************** + * + * Module Name: dswstate - Dispatcher parse tree walk management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "namesp.h" +#include "interp.h" + +#define _COMPONENT DISPATCHER + MODULE_NAME ("dswstate"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_result_stack_clear + * + * PARAMETERS: Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Reset this walk's result stack pointers to zero, thus setting + * the stack to zero. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_result_stack_clear ( + ACPI_WALK_STATE *walk_state) +{ + + walk_state->num_results = 0; + walk_state->current_result = 0; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_result_stack_push + * + * PARAMETERS: Object - Object to push + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto this walk's result stack + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_result_stack_push ( + void *object, + ACPI_WALK_STATE *walk_state) +{ + + + if (walk_state->num_results >= OBJ_NUM_OPERANDS) { + return AE_STACK_OVERFLOW; + } + + walk_state->results [walk_state->num_results] = object; + walk_state->num_results++; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_result_stack_pop + * + * PARAMETERS: Object - Where to return the popped object + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off the bottom of this walk's result stack. In + * other words, this is a FIFO. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_result_stack_pop ( + ACPI_OBJECT_INTERNAL **object, + ACPI_WALK_STATE *walk_state) +{ + + + /* Check for stack underflow */ + + if (walk_state->num_results == 0) { + return AE_AML_NO_OPERAND; + } + + + /* Pop the stack */ + + walk_state->num_results--; + + /* Check for a valid result object */ + + if (!walk_state->results [walk_state->num_results]) { + return AE_AML_NO_OPERAND; + } + + *object = walk_state->results [walk_state->num_results]; + walk_state->results [walk_state->num_results] = NULL; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_delete_all + * + * PARAMETERS: Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Clear the object stack by deleting all objects that are on it. + * Should be used with great care, if at all! + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_obj_stack_delete_all ( + ACPI_WALK_STATE *walk_state) +{ + u32 i; + + + /* The stack size is configurable, but fixed */ + + for (i = 0; i < OBJ_NUM_OPERANDS; i++) { + if (walk_state->operands[i]) { + acpi_cm_remove_reference (walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_push + * + * PARAMETERS: Object - Object to push + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto this walk's object/operand stack + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_obj_stack_push ( + void *object, + ACPI_WALK_STATE *walk_state) +{ + + + /* Check for stack overflow */ + + if (walk_state->num_operands >= OBJ_NUM_OPERANDS) { + return AE_STACK_OVERFLOW; + } + + /* Put the object onto the stack */ + + walk_state->operands [walk_state->num_operands] = object; + walk_state->num_operands++; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_pop_object + * + * PARAMETERS: Pop_count - Number of objects/entries to pop + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT + * deleted by this routine. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_obj_stack_pop_object ( + ACPI_OBJECT_INTERNAL **object, + ACPI_WALK_STATE *walk_state) +{ + + + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + return AE_AML_NO_OPERAND; + } + + + /* Pop the stack */ + + walk_state->num_operands--; + + /* Check for a valid operand */ + + if (!walk_state->operands [walk_state->num_operands]) { + return AE_AML_NO_OPERAND; + } + + /* Get operand and set stack entry to null */ + + *object = walk_state->operands [walk_state->num_operands]; + walk_state->operands [walk_state->num_operands] = NULL; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_pop + * + * PARAMETERS: Pop_count - Number of objects/entries to pop + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT + * deleted by this routine. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_obj_stack_pop ( + u32 pop_count, + ACPI_WALK_STATE *walk_state) +{ + u32 i; + + + for (i = 0; i < pop_count; i++) { + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + return AE_STACK_UNDERFLOW; + } + + /* Just set the stack entry to null */ + + walk_state->num_operands--; + walk_state->operands [walk_state->num_operands] = NULL; + } + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_pop_and_delete + * + * PARAMETERS: Pop_count - Number of objects/entries to pop + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack and delete each object that is + * popped off. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ds_obj_stack_pop_and_delete ( + u32 pop_count, + ACPI_WALK_STATE *walk_state) +{ + u32 i; + ACPI_OBJECT_INTERNAL *obj_desc; + + + for (i = 0; i < pop_count; i++) { + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + return AE_STACK_UNDERFLOW; + } + + /* Pop the stack and delete an object if present in this stack entry */ + + walk_state->num_operands--; + obj_desc = walk_state->operands [walk_state->num_operands]; + if (obj_desc) { + acpi_cm_remove_reference (walk_state->operands [walk_state->num_operands]); + walk_state->operands [walk_state->num_operands] = NULL; + } + } + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_obj_stack_get_value + * + * PARAMETERS: Index - Stack index whose value is desired. Based + * on the top of the stack (index=0 == top) + * Walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Retrieve an object from this walk's object stack. Index must + * be within the range of the current stack pointer. + * + ******************************************************************************/ + +void * +acpi_ds_obj_stack_get_value ( + u32 index, + ACPI_WALK_STATE *walk_state) +{ + + + /* Can't do it if the stack is empty */ + + if (walk_state->num_operands == 0) { + return (NULL); + } + + /* or if the index is past the top of the stack */ + + if (index > (walk_state->num_operands - (u32) 1)) { + return (NULL); + } + + + return (walk_state->operands[(NATIVE_UINT)(walk_state->num_operands - 1) - + index]); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_get_current_walk_state + * + * PARAMETERS: Walk_list - Get current active state for this walk list + * + * RETURN: Pointer to the current walk state + * + * DESCRIPTION: Get the walk state that is at the head of the list (the "current" + * walk state. + * + ******************************************************************************/ + +ACPI_WALK_STATE * +acpi_ds_get_current_walk_state ( + ACPI_WALK_LIST *walk_list) + +{ + + if (!walk_list) { + return NULL; + } + + return walk_list->walk_state; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_push_walk_state + * + * PARAMETERS: Walk_state - State to push + * Walk_list - The list that owns the walk stack + * + * RETURN: None + * + * DESCRIPTION: Place the Walk_state at the head of the state list. + * + ******************************************************************************/ + +void +acpi_ds_push_walk_state ( + ACPI_WALK_STATE *walk_state, + ACPI_WALK_LIST *walk_list) +{ + + + walk_state->next = walk_list->walk_state; + walk_list->walk_state = walk_state; + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_pop_walk_state + * + * PARAMETERS: Walk_list - The list that owns the walk stack + * + * RETURN: A Walk_state object popped from the stack + * + * DESCRIPTION: Remove and return the walkstate object that is at the head of + * the walk stack for the given walk list. NULL indicates that + * the list is empty. + * + ******************************************************************************/ + +ACPI_WALK_STATE * +acpi_ds_pop_walk_state ( + ACPI_WALK_LIST *walk_list) +{ + ACPI_WALK_STATE *walk_state; + + + walk_state = walk_list->walk_state; + + if (walk_state) { + /* Next walk state becomes the current walk state */ + + walk_list->walk_state = walk_state->next; + + /* + * Don't clear the NEXT field, this serves as an indicator + * that there is a parent WALK STATE + * Walk_state->Next = NULL; + */ + } + + return (walk_state); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_create_walk_state + * + * PARAMETERS: Origin - Starting point for this walk + * Walk_list - Owning walk list + * + * RETURN: Pointer to the new walk state. + * + * DESCRIPTION: Allocate and initialize a new walk state. The current walk state + * is set to this new state. + * + ******************************************************************************/ + +ACPI_WALK_STATE * +acpi_ds_create_walk_state ( + ACPI_OWNER_ID owner_id, + ACPI_GENERIC_OP *origin, + ACPI_OBJECT_INTERNAL *mth_desc, + ACPI_WALK_LIST *walk_list) +{ + ACPI_WALK_STATE *walk_state; + + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + acpi_gbl_walk_state_cache_requests++; + + /* Check the cache first */ + + if (acpi_gbl_walk_state_cache) { + /* There is an object available, use it */ + + walk_state = acpi_gbl_walk_state_cache; + acpi_gbl_walk_state_cache = walk_state->next; + + acpi_gbl_walk_state_cache_hits++; + acpi_gbl_walk_state_cache_depth--; + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + + else { + /* The cache is empty, create a new object */ + + /* Avoid deadlock with Acpi_cm_callocate */ + acpi_cm_release_mutex (ACPI_MTX_CACHES); + + walk_state = acpi_cm_callocate (sizeof (ACPI_WALK_STATE)); + if (!walk_state) { + return (NULL); + } + } + + walk_state->data_type = ACPI_DESC_TYPE_WALK; + walk_state->owner_id = owner_id; + walk_state->origin = origin; + walk_state->method_desc = mth_desc; + + /* Init the method args/local */ + + acpi_ds_method_data_init (walk_state); + + /* Put the new state at the head of the walk list */ + + acpi_ds_push_walk_state (walk_state, walk_list); + + return (walk_state); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ds_delete_walk_state + * + * PARAMETERS: Walk_state - State to delete + * + * RETURN: Status + * + * DESCRIPTION: Delete a walk state including all internal data structures + * + ******************************************************************************/ + +void +acpi_ds_delete_walk_state ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_GENERIC_STATE *state; + + + if (!walk_state) { + return; + } + + if (walk_state->data_type != ACPI_DESC_TYPE_WALK) { + return; + } + + /* Always must free any linked control states */ + + while (walk_state->control_state) { + state = walk_state->control_state; + walk_state->control_state = state->common.next; + + acpi_cm_delete_generic_state (state); + } + + + /* Always must free any linked parse states */ + + while (walk_state->scope_info) { + state = walk_state->scope_info; + walk_state->scope_info = state->common.next; + + acpi_cm_delete_generic_state (state); + } + + /* If walk cache is full, just free this wallkstate object */ + + if (acpi_gbl_walk_state_cache_depth >= MAX_WALK_CACHE_DEPTH) { + acpi_cm_free (walk_state); + } + + /* Otherwise put this object back into the cache */ + + else { + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + + /* Clear the state */ + + MEMSET (walk_state, 0, sizeof (ACPI_WALK_STATE)); + walk_state->data_type = ACPI_DESC_TYPE_WALK; + + /* Put the object at the head of the global cache list */ + + walk_state->next = acpi_gbl_walk_state_cache; + acpi_gbl_walk_state_cache = walk_state; + acpi_gbl_walk_state_cache_depth++; + + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ds_delete_walk_state_cache + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_ds_delete_walk_state_cache ( + void) +{ + ACPI_WALK_STATE *next; + + + /* Traverse the global cache list */ + + while (acpi_gbl_walk_state_cache) { + /* Delete one cached state object */ + + next = acpi_gbl_walk_state_cache->next; + acpi_cm_free (acpi_gbl_walk_state_cache); + acpi_gbl_walk_state_cache = next; + } + + return; +} + + diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c new file mode 100644 index 000000000..d3ad64408 --- /dev/null +++ b/drivers/acpi/events/evevent.c @@ -0,0 +1,679 @@ +/****************************************************************************** + * + * Module Name: evevent - Fixed and General Purpose Acpi_event + * handling and dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "hardware.h" +#include "events.h" +#include "namesp.h" +#include "common.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evevent"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_fixed_event_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the Fixed Acpi_event data structures + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_fixed_event_initialize(void) +{ + int i = 0; + + /* Initialize the structure that keeps track of fixed event handlers */ + + for (i = 0; i < NUM_FIXED_EVENTS; i++) { + acpi_gbl_fixed_event_handlers[i].handler = NULL; + acpi_gbl_fixed_event_handlers[i].context = NULL; + } + + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_PMTIMER + + TMR_EN, 0); + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_GLOBAL + + TMR_EN, 0); + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_POWER_BUTTON + + TMR_EN, 0); + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_SLEEP_BUTTON + + TMR_EN, 0); + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, ACPI_EVENT_RTC + + TMR_EN, 0); + + return AE_OK; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_fixed_event_detect + * + * PARAMETERS: None + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Checks the PM status register for fixed events + * + ******************************************************************************/ + +u32 +acpi_ev_fixed_event_detect(void) +{ + u32 int_status = INTERRUPT_NOT_HANDLED; + u32 status_register = 0; + u32 enable_register = 0; + + /* + * Read the fixed feature status and enable registers, as all the cases + * depend on their values. + */ + + status_register = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk); + if (acpi_gbl_FACP->pm1b_evt_blk) { + status_register |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk); + } + + enable_register = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)); + if (acpi_gbl_FACP->pm1b_evt_blk) { + enable_register |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)); + } + + /* power management timer roll over */ + + if ((status_register & ACPI_STATUS_PMTIMER) && + (enable_register & ACPI_ENABLE_PMTIMER)) + { + int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_PMTIMER); + } + + /* global event (BIOS want's the global lock) */ + + if ((status_register & ACPI_STATUS_GLOBAL) && + (enable_register & ACPI_ENABLE_GLOBAL)) + { + int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_GLOBAL); + } + + /* power button event */ + + if ((status_register & ACPI_STATUS_POWER_BUTTON) && + (enable_register & ACPI_ENABLE_POWER_BUTTON)) + { + int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_POWER_BUTTON); + } + + /* sleep button event */ + + if ((status_register & ACPI_STATUS_SLEEP_BUTTON) && + (enable_register & ACPI_ENABLE_SLEEP_BUTTON)) + { + int_status |= acpi_ev_fixed_event_dispatch (ACPI_EVENT_SLEEP_BUTTON); + } + + return int_status; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_fixed_event_dispatch + * + * PARAMETERS: Event - Event type + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Clears the status bit for the requested event, calls the + * handler that previously registered for the event. + * + ******************************************************************************/ + +u32 +acpi_ev_fixed_event_dispatch ( + u32 event) +{ + /* Clear the status bit */ + + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, (s32)TMR_STS + + event, 1); + + /* + * Make sure we've got a handler. If not, report an error. + * The event is disabled to prevent further interrupts. + */ + if (NULL == acpi_gbl_fixed_event_handlers[event].handler) { + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK, + TMR_EN + event, 0); + + REPORT_ERROR("No installed handler for fixed event."); + return INTERRUPT_NOT_HANDLED; + } + + /* Invoke the handler */ + + return (acpi_gbl_fixed_event_handlers[event].handler)( + acpi_gbl_fixed_event_handlers[event].context); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_gpe_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the GPE data structures + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_gpe_initialize (void) +{ + u32 i; + u32 j; + u32 register_index; + u32 gpe_number; + u16 gpe0register_count; + u16 gpe1_register_count; + + + /* + * Setup various GPE counts + */ + + gpe0register_count = (u16) DIV_2 (acpi_gbl_FACP->gpe0blk_len); + gpe1_register_count = (u16) DIV_2 (acpi_gbl_FACP->gpe1_blk_len); + acpi_gbl_gpe_register_count = gpe0register_count + gpe1_register_count; + + /* + * Allocate the Gpe information block + */ + + acpi_gbl_gpe_registers = acpi_cm_callocate (acpi_gbl_gpe_register_count * + sizeof (ACPI_GPE_REGISTERS)); + if (!acpi_gbl_gpe_registers) { + return (AE_NO_MEMORY); + } + + /* + * Allocate the Gpe dispatch handler block + * There are eight distinct GP events per register. + * Initialization to zeros is sufficient + */ + + acpi_gbl_gpe_info = acpi_cm_callocate (MUL_8 (acpi_gbl_gpe_register_count) * + sizeof (ACPI_GPE_LEVEL_INFO)); + if (!acpi_gbl_gpe_info) { + acpi_cm_free (acpi_gbl_gpe_registers); + return (AE_NO_MEMORY); + } + + /* Set the Gpe validation table to GPE_INVALID */ + + MEMSET (acpi_gbl_gpe_valid, (int) ACPI_GPE_INVALID, NUM_GPE); + + /* + * Initialize the Gpe information and validation blocks. A goal of these + * blocks is to hide the fact that there are two separate GPE register sets + * In a given block, the status registers occupy the first half, and + * the enable registers occupy the second half. + */ + + /* GPE Block 0 */ + + register_index = 0; + + for (i = 0; i < gpe0register_count; i++) { + acpi_gbl_gpe_registers[register_index].status_addr = + (u16) (acpi_gbl_FACP->gpe0blk + i); + + acpi_gbl_gpe_registers[register_index].enable_addr = + (u16) (acpi_gbl_FACP->gpe0blk + i + gpe0register_count); + + acpi_gbl_gpe_registers[register_index].gpe_base = (u8) MUL_8 (i); + + for (j = 0; j < 8; j++) { + gpe_number = acpi_gbl_gpe_registers[register_index].gpe_base + j; + acpi_gbl_gpe_valid[gpe_number] = (u8) register_index; + } + + /* + * Clear the status/enable registers. Note that status registers + * are cleared by writing a '1', while enable registers are cleared + * by writing a '0'. + */ + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00); + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF); + + register_index++; + } + + /* GPE Block 1 */ + + for (i = 0; i < gpe1_register_count; i++) { + acpi_gbl_gpe_registers[register_index].status_addr = + (u16) (acpi_gbl_FACP->gpe1_blk + i); + + acpi_gbl_gpe_registers[register_index].enable_addr = + (u16) (acpi_gbl_FACP->gpe1_blk + i + gpe1_register_count); + + acpi_gbl_gpe_registers[register_index].gpe_base = + (u8) (acpi_gbl_FACP->gpe1_base + MUL_8 (i)); + + for (j = 0; j < 8; j++) { + gpe_number = acpi_gbl_gpe_registers[register_index].gpe_base + j; + acpi_gbl_gpe_valid[gpe_number] = (u8) register_index; + } + + /* + * Clear the status/enable registers. Note that status registers + * are cleared by writing a '1', while enable registers are cleared + * by writing a '0'. + */ + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, 0x00); + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].status_addr, 0xFF); + + register_index++; + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_save_method_info + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Called from Acpi_walk_namespace. Expects each object to be a + * control method under the _GPE portion of the namespace. + * Extract the name and GPE type from the object, saving this + * information for quick lookup during GPE dispatch + * + * The name of each GPE control method is of the form: + * "_Lnn" or "_Enn" + * Where: + * L - means that the GPE is level triggered + * E - means that the GPE is edge triggered + * nn - is the GPE number + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_save_method_info ( + ACPI_HANDLE obj_handle, + u32 level, + void *obj_desc, + void **return_value) +{ + u32 gpe_number; + char name[ACPI_NAME_SIZE + 1]; + u8 type; + + + /* Extract the name from the object and convert to a string */ + + MOVE_UNALIGNED32_TO_32 (name, &((ACPI_NAMED_OBJECT*) obj_handle)->name); + name[ACPI_NAME_SIZE] = 0; + + /* + * Edge/Level determination is based on the 2nd char of the method name + */ + if (name[1] == 'L') { + type = ACPI_EVENT_LEVEL_TRIGGERED; + } + else if (name[1] == 'E') { + type = ACPI_EVENT_EDGE_TRIGGERED; + } + else { + /* Unknown method type, just ignore it! */ + + return AE_OK; + } + + /* Convert the last two characters of the name to the Gpe Number */ + + gpe_number = STRTOUL (&name[2], NULL, 16); + if (gpe_number == ACPI_UINT32_MAX) { + /* Conversion failed; invalid method, just ignore it */ + + return AE_OK; + } + + /* Ensure that we have a valid GPE number */ + + if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { + /* Not valid, all we can do here is ignore it */ + + return AE_OK; + } + + /* + * Now we can add this information to the Gpe_info block + * for use during dispatch of this GPE. + */ + + acpi_gbl_gpe_info [gpe_number].type = type; + acpi_gbl_gpe_info [gpe_number].method_handle = obj_handle; + + + /* + * Enable the GPE (SCIs should be disabled at this point) + */ + + acpi_hw_enable_gpe (gpe_number); + + return AE_OK; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_init_gpe_control_methods + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Obtain the control methods associated with the GPEs. + * + * NOTE: Must be called AFTER namespace initialization! + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_init_gpe_control_methods (void) +{ + ACPI_STATUS status; + + + /* Get a permanent handle to the _GPE object */ + + status = acpi_get_handle (NULL, "\\_GPE", &acpi_gbl_gpe_obj_handle); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Traverse the namespace under \_GPE to find all methods there */ + + status = acpi_walk_namespace (ACPI_TYPE_METHOD, acpi_gbl_gpe_obj_handle, + ACPI_INT32_MAX, acpi_ev_save_method_info, + NULL, NULL); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_gpe_cleanup + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Cleanup in preparation for unload. + * + ******************************************************************************/ + +void +acpi_ev_gpe_cleanup (void) +{ + + acpi_cm_free (acpi_gbl_gpe_registers); + acpi_cm_free (acpi_gbl_gpe_info); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_gpe_detect + * + * PARAMETERS: None + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Detect if any GP events have occurred + * + ******************************************************************************/ + +u32 +acpi_ev_gpe_detect (void) +{ + u32 int_status = INTERRUPT_NOT_HANDLED; + u32 i; + u32 j; + u8 enabled_status_byte; + u8 bit_mask; + + + /* + * Read all of the 8-bit GPE status and enable registers + * in both of the register blocks, saving all of it. + * Find all currently active GP events. + */ + + for (i = 0; i < acpi_gbl_gpe_register_count; i++) { + acpi_gbl_gpe_registers[i].status = + acpi_os_in8 (acpi_gbl_gpe_registers[i].status_addr); + + acpi_gbl_gpe_registers[i].enable = + acpi_os_in8 (acpi_gbl_gpe_registers[i].enable_addr); + + /* First check if there is anything active at all in this register */ + + enabled_status_byte = (u8) (acpi_gbl_gpe_registers[i].status & + acpi_gbl_gpe_registers[i].enable); + + if (!enabled_status_byte) { + /* No active GPEs in this register, move on */ + + continue; + } + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0, bit_mask = 1; j < 8; j++, bit_mask <<= 1) { + /* Examine one GPE bit */ + + if (enabled_status_byte & bit_mask) { + /* + * Found an active GPE. Dispatch the event to a handler + * or method. + */ + int_status |= + acpi_ev_gpe_dispatch (acpi_gbl_gpe_registers[i].gpe_base + j); + } + } + } + + return int_status; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_asynch_execute_gpe_method + * + * PARAMETERS: Gpe_number - The 0-based Gpe number + * + * RETURN: None + * + * DESCRIPTION: Perform the actual execution of a GPE control method. This + * function is called from an invocation of Acpi_os_queue_for_execution + * (and therefore does NOT execute at interrupt level) so that + * the control method itself is not executed in the context of + * the SCI interrupt handler. + * + ******************************************************************************/ + +void +acpi_ev_asynch_execute_gpe_method ( + void *context) +{ + u32 gpe_number = (u32) context; + ACPI_GPE_LEVEL_INFO gpe_info; + + + /* Take a snapshot of the GPE info for this level */ + + acpi_cm_acquire_mutex (ACPI_MTX_EVENTS); + gpe_info = acpi_gbl_gpe_info [gpe_number]; + acpi_cm_release_mutex (ACPI_MTX_EVENTS); + + /* + * Function Handler (e.g. EC): + * --------------------------- + * Execute the installed function handler to handle this event. + */ + if (gpe_info.handler) { + gpe_info.handler (gpe_info.context); + } + + /* + * Method Handler (_Lxx, _Exx): + * ---------------------------- + * Acpi_evaluate the _Lxx/_Exx control method that corresponds to this GPE. + */ + else if (gpe_info.method_handle) { + acpi_ns_evaluate_by_handle (gpe_info.method_handle, NULL, NULL); + } + + /* + * Level-Triggered? + * ---------------- + * If level-triggered, clear the GPE status bit after execution. Note + * that edge-triggered events are cleared prior to calling (via DPC) + * this function. + */ + if (gpe_info.type | ACPI_EVENT_LEVEL_TRIGGERED) { + acpi_hw_clear_gpe (gpe_number); + } + + /* + * Enable the GPE. + */ + acpi_hw_enable_gpe (gpe_number); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_gpe_dispatch + * + * PARAMETERS: Gpe_number - The 0-based Gpe number + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Handle and dispatch a General Purpose Acpi_event. + * Clears the status bit for the requested event. + * + * TBD: [Investigate] is this still valid or necessary: + * The Gpe handler differs from the fixed events in that it clears the enable + * bit rather than the status bit to clear the interrupt. This allows + * software outside of interrupt context to determine what caused the SCI and + * dispatch the correct AML. + * + ******************************************************************************/ + +u32 +acpi_ev_gpe_dispatch ( + u32 gpe_number) +{ + + /*DEBUG_INCREMENT_EVENT_COUNT (EVENT_GENERAL);*/ + + /* Ensure that we have a valid GPE number */ + + if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { + return (INTERRUPT_NOT_HANDLED); + } + + /* + * Disable the GPE. + */ + acpi_hw_disable_gpe (gpe_number); + + /* + * Edge-Triggered? + * --------------- + * If edge-triggered, clear the GPE status bit now. Note that + * level-triggered events are cleared after the GPE is serviced + * (see Acpi_ev_asynch_execute_gpe_method). + */ + if (acpi_gbl_gpe_info [gpe_number].type | ACPI_EVENT_EDGE_TRIGGERED) { + acpi_hw_clear_gpe (gpe_number); + } + + /* + * Queue-up the Handler: + * --------------------- + * Queue the handler, which is either an installable function handler + * (e.g. EC) or a control method (e.g. _Lxx/_Exx) for later execution. + */ + if (acpi_gbl_gpe_info [gpe_number].handler || + acpi_gbl_gpe_info [gpe_number].method_handle) + { + if (ACPI_FAILURE (acpi_os_queue_for_execution (OSD_PRIORITY_GPE, + acpi_ev_asynch_execute_gpe_method, + (void*)(NATIVE_UINT)gpe_number))) + { + /* + * Shoudn't occur, but if it does report an error. Note that + * the GPE will remain disabled until the ACPI Core Subsystem + * is restarted, or the handler is removed/reinstalled. + */ + REPORT_ERROR ("Unable to queue-up handler for GPE."); + } + } + + /* + * Non Handled GPEs: + * ----------------- + * GPEs without handlers are disabled and kept that way until a handler + * is registered for them. + */ + else { + REPORT_ERROR ("No installed handler for GPE."); + } + + return (INTERRUPT_HANDLED); +} diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c new file mode 100644 index 000000000..5164d3f27 --- /dev/null +++ b/drivers/acpi/events/evmisc.c @@ -0,0 +1,357 @@ +/****************************************************************************** + * + * Module Name: evmisc - ACPI device notification handler dispatch + * and ACPI Global Lock support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "events.h" +#include "namesp.h" +#include "interp.h" +#include "hardware.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evmisc"); + + +/************************************************************************** + * + * FUNCTION: Acpi_ev_notify_dispatch + * + * PARAMETERS: + * + * RETURN: None. + * + * DESCRIPTION: Dispatch a device notification event to a previously + * installed handler. + * + *************************************************************************/ + +void +acpi_ev_notify_dispatch ( + ACPI_HANDLE device, + u32 notify_value) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *handler_obj; + NOTIFY_HANDLER handler; + + + /* + * For value 1 (Ejection Request), some device method may need to be run. + * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need to be run. + * For value 0x80 (Status Change) on the power button or sleep button, + * initiate soft-off or sleep operation? + */ + + + switch (notify_value) + { + case 0: + break; + + case 1: + break; + + case 2: + break; + + case 0x80: + break; + + default: + break; + } + + + /* + * Invoke a global notify handler if installed. + * This is done _before_ we invoke the per-device handler attached to the device. + */ + + if (notify_value <= MAX_SYS_NOTIFY) { + /* Global system notification handler */ + + if (acpi_gbl_sys_notify.handler) { + acpi_gbl_sys_notify.handler (device, notify_value, + acpi_gbl_sys_notify.context); + } + } + + else { + /* Global driver notification handler */ + + if (acpi_gbl_drv_notify.handler) { + acpi_gbl_drv_notify.handler (device, notify_value, + acpi_gbl_drv_notify.context); + } + } + + + /* + * Get the notify object which must be attached to the device NTE + */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) device); + if (!obj_desc) { + /* There can be no notify handler for this device */ + + return; + } + + + /* We have the notify object, Get the right handler */ + + if (notify_value <= MAX_SYS_NOTIFY) { + handler_obj = obj_desc->device.sys_handler; + } + else { + handler_obj = obj_desc->device.drv_handler; + } + + /* Validate the handler */ + + if (!handler_obj) { + /* There is no notify handler for this device */ + + return; + } + + /* There is a handler, invoke it */ + + handler = handler_obj->notify_handler.handler; + handler (device, notify_value, handler_obj->notify_handler.context); + +} + + +/*************************************************************************** + * + * FUNCTION: Acpi_ev_global_lock_thread + * + * RETURN: None + * + * DESCRIPTION: Invoked by SCI interrupt handler upon acquisition of the + * Global Lock. Simply signal all threads that are waiting + * for the lock. + * + **************************************************************************/ + +void +acpi_ev_global_lock_thread ( + void *context) +{ + + /* Signal threads that are waiting for the lock */ + + if (acpi_gbl_global_lock_thread_count) { + /* Send sufficient units to the semaphore */ + + acpi_os_signal_semaphore (acpi_gbl_global_lock_semaphore, + acpi_gbl_global_lock_thread_count); + } +} + + +/*************************************************************************** + * + * FUNCTION: Acpi_ev_global_lock_handler + * + * RETURN: Status + * + * DESCRIPTION: Invoked directly from the SCI handler when a global lock + * release interrupt occurs. Grab the global lock and queue + * the global lock thread for execution + * + **************************************************************************/ + +u32 +acpi_ev_global_lock_handler ( + void *context) +{ + u8 acquired = FALSE; + void *global_lock; + + + /* + * Attempt to get the lock + * If we don't get it now, it will be marked pending and we will + * take another interrupt when it becomes free. + */ + + global_lock = &acpi_gbl_FACS->global_lock; + ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired); + if (acquired) { + /* Got the lock, now wake all threads waiting for it */ + + acpi_gbl_global_lock_acquired = TRUE; + + /* Run the Global Lock thread which will signal all waiting threads */ + + acpi_os_queue_for_execution (OSD_PRIORITY_HIGH, acpi_ev_global_lock_thread, + context); + } + + return INTERRUPT_HANDLED; +} + + +/*************************************************************************** + * + * FUNCTION: Acpi_ev_init_global_lock_handler + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the global lock release event + * + **************************************************************************/ + +ACPI_STATUS +acpi_ev_init_global_lock_handler (void) +{ + ACPI_STATUS status; + + + status = acpi_install_fixed_event_handler (ACPI_EVENT_GLOBAL, + acpi_ev_global_lock_handler, NULL); + + return (status); +} + + +/*************************************************************************** + * + * FUNCTION: Acpi_ev_acquire_global_lock + * + * RETURN: Status + * + * DESCRIPTION: Attempt to gain ownership of the Global Lock. + * + **************************************************************************/ + +ACPI_STATUS +acpi_ev_acquire_global_lock(void) +{ + ACPI_STATUS status = AE_OK; + u8 acquired = FALSE; + void *global_lock; + + + /* One more thread wants the global lock */ + + acpi_gbl_global_lock_thread_count++; + + + /* If we (OS side) have the hardware lock already, we are done */ + + if (acpi_gbl_global_lock_acquired) { + return (AE_OK); + } + + /* Only if the FACS is valid */ + + if (!acpi_gbl_FACS) { + return (AE_OK); + } + + + /* We must acquire the actualy hardware lock */ + + global_lock = &acpi_gbl_FACS->global_lock; + ACPI_ACQUIRE_GLOBAL_LOCK (global_lock, acquired); + if (acquired) { + /* We got the lock */ + + acpi_gbl_global_lock_acquired = TRUE; + + return (AE_OK); + } + + + /* + * Did not get the lock. The pending bit was set above, and we must now + * wait until we get the global lock released interrupt. + */ + + /* + * Acquire the global lock semaphore first. + * Since this wait will block, we must release the interpreter + */ + + acpi_aml_exit_interpreter (); + status = acpi_aml_system_wait_semaphore (acpi_gbl_global_lock_semaphore, + ACPI_UINT32_MAX); + acpi_aml_enter_interpreter (); + + + return (status); +} + + +/*************************************************************************** + * + * FUNCTION: Acpi_ev_release_global_lock + * + * DESCRIPTION: Releases ownership of the Global Lock. + * + **************************************************************************/ + +void +acpi_ev_release_global_lock (void) +{ + u8 pending = FALSE; + void *global_lock; + + + if (!acpi_gbl_FACS) { + return; + } + + /* One fewer thread has the global lock */ + + acpi_gbl_global_lock_thread_count--; + + + /* Have all threads released the lock? */ + + if (!acpi_gbl_global_lock_thread_count) { + /* + * No more threads holding lock, we can do the actual hardware + * release + */ + + global_lock = &acpi_gbl_FACS->global_lock; + ACPI_RELEASE_GLOBAL_LOCK (global_lock, pending); + acpi_gbl_global_lock_acquired = FALSE; + + /* + * If the pending bit was set, we must write GBL_RLS to the control + * register + */ + if (pending) { + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, + (s32)PM1_CONTROL | GBL_RLS, 1); + } + } + + return; +} diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c new file mode 100644 index 000000000..3e2a91d32 --- /dev/null +++ b/drivers/acpi/events/evregion.c @@ -0,0 +1,690 @@ +/****************************************************************************** + * + * Module Name: evregion - ACPI Address_space / Op_region handler dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "events.h" +#include "namesp.h" +#include "interp.h" +#include "amlcode.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evregion"); + + +#define PCI_ROOT_HID_STRING "PNP0A03" +#define PCI_ROOT_HID_VALUE 0x030AD041 /* EISAID("PNP0A03") */ + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_find_one_pci_root_bus + * + * PARAMETERS: + * + * RETURN: None + * + * DESCRIPTION: + * + *****************************************************************************/ + +ACPI_STATUS +acpi_ev_find_one_pci_root_bus ( + ACPI_HANDLE obj_handle, + u32 nesting_level, + void *context, + void **return_value) +{ + ACPI_NAMED_OBJECT *entry; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + entry = (ACPI_NAMED_OBJECT*) obj_handle; + obj_desc = ((ACPI_NAMED_OBJECT*)obj_handle)->object; + + + /* + * We are looking for all valid _HID objects. + */ + + if (STRNCMP ((char *)&entry->name, METHOD_NAME__HID, ACPI_NAME_SIZE) || + (!obj_desc)) + { + return AE_OK; + } + + + /* + * Found an _HID object. + * Now we need a HID with the value EISAID("PNP0A03") + * HID can be either a number or a string. + */ + + switch (obj_desc->common.type) + { + case ACPI_TYPE_NUMBER: + + if (obj_desc->number.value != PCI_ROOT_HID_VALUE) { + return AE_OK; + } + + break; + + case ACPI_TYPE_STRING: + + if (STRNCMP (obj_desc->string.pointer, PCI_ROOT_HID_STRING, + sizeof (PCI_ROOT_HID_STRING))) + { + return AE_OK; + } + + break; + + default: + + return AE_OK; + } + + + /* + * We found a valid PCI_ROOT_HID. + * The parent of the HID entry is the PCI device; Install the default PCI + * handler for this PCI device. + */ + + status = acpi_install_address_space_handler (acpi_ns_get_parent_entry (entry), + ADDRESS_SPACE_PCI_CONFIG, + ACPI_DEFAULT_HANDLER, NULL, NULL); + + return AE_OK; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_find_pci_root_buses + * + * PARAMETERS: + * + * RETURN: None + * + * DESCRIPTION: + * + *****************************************************************************/ + +ACPI_STATUS +acpi_ev_find_pci_root_buses ( + void) +{ + + acpi_ns_walk_namespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + FALSE, acpi_ev_find_one_pci_root_bus, NULL, NULL); + + return AE_OK; +} + + +/************************************************************************** + * + * FUNCTION: Acpi_ev_install_default_address_space_handlers + * + * PARAMETERS: + * + * RETURN: Status + * + * DESCRIPTION: Installs the core subsystem address space handlers. + * + *************************************************************************/ + +ACPI_STATUS +acpi_ev_install_default_address_space_handlers ( + void) +{ + ACPI_STATUS status; + + + /* + * NOTE: All address spaces (PCI Config, EC, SMBus) are scope dependent + * and registration must occur for a specific device. In the case + * system memory and IO address spaces there is currently no device + * associated with the address space. For these we use the root. + */ + + status = acpi_install_address_space_handler (acpi_gbl_root_object, + ADDRESS_SPACE_SYSTEM_MEMORY, + ACPI_DEFAULT_HANDLER, NULL, NULL); + if (ACPI_FAILURE (status)) { + return (status); + } + + status = acpi_install_address_space_handler (acpi_gbl_root_object, + ADDRESS_SPACE_SYSTEM_IO, + ACPI_DEFAULT_HANDLER, NULL, NULL); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Install PCI config space handler for all PCI root bridges. A PCI root + * bridge is found by searching for devices containing a HID with the value + * EISAID("PNP0A03") + */ + + acpi_ev_find_pci_root_buses (); + + + return (AE_OK); +} + + +/* TBD: [Restructure] Move to the methods directory */ + +/************************************************************************** + * + * FUNCTION: Acpi_ev_execute_reg_method + * + * PARAMETERS: Region_obj - Object structure + * Function - On (1) or Off (0) + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG method for a region + * + *************************************************************************/ + +ACPI_STATUS +acpi_ev_execute_reg_method ( + ACPI_OBJECT_INTERNAL *region_obj, + u32 function) +{ + ACPI_OBJECT_INTERNAL *params[3]; + ACPI_OBJECT_INTERNAL space_iD_obj; + ACPI_OBJECT_INTERNAL function_obj; + ACPI_STATUS status; + + + if (region_obj->region.REGmethod == NULL) { + return (AE_OK); + } + + /* + * _REG method has two arguments + * Arg0: Integer: Operation region space ID + * Same value as Region_obj->Region.Space_id + * Arg1: Integer: connection status + * 1 for connecting the handler, + * 0 for disconnecting the handler + * Passed as a parameter + */ + + acpi_cm_init_static_object (&space_iD_obj); + acpi_cm_init_static_object (&function_obj); + + /* + * Method requires two parameters. + */ + params [0] = &space_iD_obj; + params [1] = &function_obj; + params [2] = NULL; + + /* + * Set up the parameter objects + */ + space_iD_obj.common.type = ACPI_TYPE_NUMBER; + space_iD_obj.number.value = region_obj->region.space_id; + + function_obj.common.type = ACPI_TYPE_NUMBER; + function_obj.number.value = function; + + /* + * Execute the method, no return value + */ + status = acpi_ns_evaluate_by_handle (region_obj->region.REGmethod, params, NULL); + return (status); +} + + +/************************************************************************** + * + * FUNCTION: Acpi_ev_address_space_dispatch + * + * PARAMETERS: Region_obj - internal region object + * Space_id - ID of the address space (0-255) + * Function - Read or Write operation + * Address - Where in the space to read or write + * Bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * + * RETURN: Status + * + * DESCRIPTION: Dispatch an address space or operation region access to + * a previously installed handler. + * + *************************************************************************/ + +ACPI_STATUS +acpi_ev_address_space_dispatch ( + ACPI_OBJECT_INTERNAL *region_obj, + u32 function, + u32 address, + u32 bit_width, + u32 *value) +{ + ACPI_STATUS status; + ADDRESS_SPACE_HANDLER handler; + ADDRESS_SPACE_SETUP region_setup; + ACPI_OBJECT_INTERNAL *handler_desc; + void *region_context; + + + /* + * Check for an installed handler + */ + handler_desc = region_obj->region.addr_handler; + + if (!handler_desc) { + return(AE_EXIST); + } + + /* + * It may be the case that the region has never been initialized + * Some types of regions require special init code + */ + if (!(region_obj->region.region_flags & REGION_INITIALIZED)) { + /* + * This region has not been initialized yet, do it + */ + region_setup = handler_desc->addr_handler.setup; + if (!region_setup) { + /* + * Bad news, no init routine and not init'd + */ + return (AE_UNKNOWN_STATUS); + } + + /* + * We must exit the interpreter because the region setup will potentially + * execute control methods + */ + acpi_aml_exit_interpreter (); + + status = region_setup (region_obj, ACPI_REGION_ACTIVATE, + handler_desc->addr_handler.context, + ®ion_context); + + /* Re-enter the interpreter */ + + acpi_aml_enter_interpreter (); + + /* + * Init routine may fail + */ + if (ACPI_FAILURE (status)) { + return(status); + } + + /* + * Save the returned context for use in all accesses to the region + */ + handler_desc->addr_handler.context = region_context; + } + + /* + * We have everything we need, begin the process + */ + handler = handler_desc->addr_handler.handler; + + if (!(handler_desc->addr_handler.flags & ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* + * For handlers other than the default (supplied) handlers, we must + * exit the interpreter because the handler *might* block -- we don't + * know what it will do, so we can't hold the lock on the intepreter. + */ + acpi_aml_exit_interpreter(); + } + + /* + * Invoke the handler. + */ + status = handler (function, address, bit_width, value, + handler_desc->addr_handler.context); + + + if (!(handler_desc->addr_handler.flags & ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* We just returned from a non-default handler, we must re-enter the + interpreter */ + + acpi_aml_enter_interpreter (); + } + + return (status); +} + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_disassociate_region_and_handler + * + * PARAMETERS: Handler_obj - Handler Object + * Region_obj - Region Object + * + * RETURN: None + * + * DESCRIPTION: Break the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +void +acpi_ev_disassociate_region_from_handler( + ACPI_OBJECT_INTERNAL *region_obj) +{ + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL **last_obj_ptr; + ADDRESS_SPACE_SETUP region_setup; + void *region_context; + ACPI_STATUS status; + + + /* + * Get the address handler from the region object + */ + + handler_obj = region_obj->region.addr_handler; + if (!handler_obj) { + /* + * This region has no handler, all done + */ + return; + } + + + /* + * Find this region in the handler's list + */ + + obj_desc = handler_obj->addr_handler.region_list; + last_obj_ptr = &handler_obj->addr_handler.region_list; + + while (obj_desc) { + /* + * See if this is the one + */ + if (obj_desc == region_obj) { + /* + * This is it, remove it from the handler's list + */ + *last_obj_ptr = obj_desc->region.link; + + /* + * Now stop region accesses by executing the _REG method + */ + acpi_ev_execute_reg_method (region_obj, 0); + + /* + * Call the setup handler with the deactivate notification + */ + region_setup = handler_obj->addr_handler.setup; + status = region_setup (region_obj, ACPI_REGION_DEACTIVATE, + handler_obj->addr_handler.context, + ®ion_context); + + /* + * Save the returned context (It is the original context + * passed into Install) + */ + handler_obj->addr_handler.context = region_context; + + /* + * Init routine may fail, Just ignore errors + */ + + /* + * Remove handler reference in the region + * + * NOTE: this doesn't mean that the region goes away + * The region is just inaccessible as indicated to + * the _REG method + * + * If the region is on the handler's list + * this better be the region's handler + */ + ACPI_ASSERT (region_obj->region.addr_handler == handler_obj); + + region_obj->region.addr_handler = NULL; + + return; + + } /* found the right handler */ + + /* + * Move through the linked list of handlers + */ + last_obj_ptr = &obj_desc->region.link; + obj_desc = obj_desc->region.link; + } + + /* + * If we get here, the region was not in the handler's region list + */ + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_associate_region_and_handler + * + * PARAMETERS: Handler_obj - Handler Object + * Region_obj - Region Object + * + * RETURN: None + * + * DESCRIPTION: Create the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_associate_region_and_handler( + ACPI_OBJECT_INTERNAL *handler_obj, + ACPI_OBJECT_INTERNAL *region_obj) +{ + ACPI_STATUS status; + + + ACPI_ASSERT (region_obj->region.space_id == handler_obj->addr_handler.space_id); + ACPI_ASSERT (region_obj->region.addr_handler == 0); + + /* + * Link this region to the front of the handler's list + */ + + region_obj->region.link = handler_obj->addr_handler.region_list; + handler_obj->addr_handler.region_list = region_obj; + + /* + * set the region's handler + */ + +/* + Handler_obj->Common.Reference_count = + (u16) (Handler_obj->Common.Reference_count + + Region_obj->Common.Reference_count - 1); +*/ + region_obj->region.addr_handler = handler_obj; + + /* + * Last thing, tell all users that this region is usable + */ + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + status = acpi_ev_execute_reg_method (region_obj, 1); + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ev_addr_handler_helper + * + * PARAMETERS: Handle - Entry to be dumped + * Level - Nesting level of the handle + * Context - Passed into Acpi_ns_walk_namespace + * + * DESCRIPTION: This routine checks to see if the object is a Region if it + * is then the address handler is installed in it. + * + * If the Object is a Device, and the device has a handler of + * the same type then the search is terminated in that branch. + * + * This is because the existing handler is closer in proximity + * to any more regions than the one we are trying to install. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ev_addr_handler_helper ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value) +{ + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_OBJECT_INTERNAL *tmp_obj; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status; + + + handler_obj = (ACPI_OBJECT_INTERNAL *) context; + + /* Parameter validation */ + + if (!handler_obj) { + return (AE_OK); + } + + /* Convert and validate the device handle */ + + obj_entry = acpi_ns_convert_handle_to_entry (obj_handle); + if (!obj_entry) { + return (AE_BAD_PARAMETER); + } + + /* + * We only care about regions.and objects + * that can have address handlers + */ + + if ((obj_entry->type != ACPI_TYPE_DEVICE) && + (obj_entry->type != ACPI_TYPE_REGION) && + (obj_entry != acpi_gbl_root_object)) + { + return (AE_OK); + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) obj_entry); + if (!obj_desc) { + /* + * The object DNE, we don't care about it + */ + return (AE_OK); + } + + /* + * Devices are handled different than regions + */ + if (IS_THIS_OBJECT_TYPE (obj_desc, ACPI_TYPE_DEVICE)) { + /* + * See if this guy has any handlers + */ + tmp_obj = obj_desc->device.addr_handler; + while (tmp_obj) { + /* + * Now let's see if it's for the same address space. + */ + if (tmp_obj->addr_handler.space_id == handler_obj->addr_handler.space_id) { + /* + * It's for the same address space + */ + + /* + * Since the object we found it on was a device, then it + * means that someone has already installed a handler for + * the branch of the namespace from this device on. Just + * bail out telling the walk routine to not traverse this + * branch. This preserves the scoping rule for handlers. + */ + return (AE_CTRL_DEPTH); + } + + /* + * Move through the linked list of handlers + */ + tmp_obj = tmp_obj->addr_handler.link; + } + + /* + * As long as the device didn't have a handler for this + * space we don't care about it. We just ignore it and + * proceed. + */ + return (AE_OK); + } + + /* + * Only here if it was a region + */ + ACPI_ASSERT (obj_desc->common.type == ACPI_TYPE_REGION); + + if (obj_desc->region.space_id != handler_obj->addr_handler.space_id) { + /* + * This region is for a different address space + * ignore it + */ + return (AE_OK); + } + + /* + * Now we have a region and it is for the handler's address + * space type. + * + * First disconnect region for any previous handler (if any) + */ + acpi_ev_disassociate_region_from_handler (obj_desc); + + /* + * Then connect the region to the new handler + */ + status = acpi_ev_associate_region_and_handler (handler_obj, obj_desc); + + return (status); +} + + diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c new file mode 100644 index 000000000..526bd036c --- /dev/null +++ b/drivers/acpi/events/evrgnini.c @@ -0,0 +1,425 @@ +/****************************************************************************** + * + * Module Name: evrgnini- ACPI Address_space / Op_region init + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "events.h" +#include "namesp.h" +#include "interp.h" +#include "amlcode.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evrgnini"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_ev_system_memory_region_setup + * + * PARAMETERS: Region_obj - region we are interested in + * Function - start or stop + * Handler_context - Address space handler context + * Returned context - context to be used with each call to the + * handler for this region + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling, a nop for now + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ev_system_memory_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context) +{ + MEM_HANDLER_CONTEXT *mem_context; + ACPI_OBJECT_INTERNAL *region_obj = (ACPI_OBJECT_INTERNAL *) handle; + + + if (function == ACPI_REGION_DEACTIVATE) { + region_obj->region.region_flags &= ~(REGION_INITIALIZED); + + *return_context = NULL; + if (handler_context) { + mem_context = handler_context; + *return_context = mem_context->handler_context; + + acpi_cm_free (mem_context); + } + return (AE_OK); + } + + + /* Activate. Create a new context */ + + mem_context = acpi_cm_callocate (sizeof (MEM_HANDLER_CONTEXT)); + if (!mem_context) { + return (AE_NO_MEMORY); + } + + /* Init. (Mapping fields are all set to zeros above) */ + + mem_context->handler_context = handler_context; + region_obj->region.region_flags |= REGION_INITIALIZED; + + *return_context = mem_context; + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ev_io_space_region_setup + * + * PARAMETERS: Region_obj - region we are interested in + * Function - start or stop + * Handler_context - Address space handler context + * Returned context - context to be used with each call to the + * handler for this region + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ev_io_space_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context) +{ + ACPI_OBJECT_INTERNAL *region_obj = (ACPI_OBJECT_INTERNAL *) handle; + + + if (function == ACPI_REGION_DEACTIVATE) { + region_obj->region.region_flags &= ~(REGION_INITIALIZED); + *return_context = handler_context; + return (AE_OK); + } + + /* Activate the region */ + + region_obj->region.region_flags |= REGION_INITIALIZED; + *return_context = handler_context; + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ev_pci_config_region_setup + * + * PARAMETERS: Region_obj - region we are interested in + * Function - start or stop + * Handler_context - Address space handler context + * Returned context - context to be used with each call to the + * handler for this region + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + * MUTEX: Assumes namespace is locked + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ev_pci_config_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context) +{ + ACPI_STATUS status = AE_OK; + u32 temp; + PCI_HANDLER_CONTEXT *pci_context; + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_NAMED_OBJECT *search_scope; + ACPI_OBJECT_INTERNAL *region_obj = (ACPI_OBJECT_INTERNAL *) handle; + + + handler_obj = region_obj->region.addr_handler; + + if (!handler_obj) { + /* + * No installed handler. This shouldn't happen because the dispatch + * routine checks before we get here, but we check again just in case. + */ + return(AE_EXIST); + } + + if (function == ACPI_REGION_DEACTIVATE) { + region_obj->region.region_flags &= ~(REGION_INITIALIZED); + + *return_context = NULL; + if (handler_context) { + pci_context = handler_context; + *return_context = pci_context->handler_context; + + acpi_cm_free (pci_context); + } + + return (status); + } + + + /* Create a new context */ + + pci_context = acpi_cm_allocate (sizeof(PCI_HANDLER_CONTEXT)); + if (!pci_context) { + return (AE_NO_MEMORY); + } + + /* + * For PCI Config space access, we have to pass the segment, bus, + * device and function numbers. This routine must acquire those. + */ + + /* + * First get device and function numbers from the _ADR object + * in the parent's scope. + */ + ACPI_ASSERT(region_obj->region.nte); + + search_scope = acpi_ns_get_parent_entry (region_obj->region.nte); + + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + /* Acpi_evaluate the _ADR object */ + + status = acpi_cm_evaluate_numeric_object (METHOD_NAME__ADR, search_scope, &temp); + /* + * The default is zero, since the allocation above zeroed the data, just + * do nothing on failures. + */ + if (ACPI_SUCCESS (status)) { + /* + * Got it.. + */ + pci_context->dev_func = temp; + } + + /* + * Get the _SEG and _BBN values from the device upon which the handler + * is installed. + * + * We need to get the _SEG and _BBN objects relative to the PCI BUS device. + * This is the device the handler has been registered to handle. + */ + + search_scope = handler_obj->addr_handler.nte; + + status = acpi_cm_evaluate_numeric_object (METHOD_NAME__SEG, search_scope, &temp); + if (ACPI_SUCCESS (status)) { + /* + * Got it.. + */ + pci_context->seg = temp; + } + + status = acpi_cm_evaluate_numeric_object (METHOD_NAME__BBN, search_scope, &temp); + if (ACPI_SUCCESS (status)) { + /* + * Got it.. + */ + pci_context->bus = temp; + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + *return_context = pci_context; + + region_obj->region.region_flags |= REGION_INITIALIZED; + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ev_default_region_setup + * + * PARAMETERS: Region_obj - region we are interested in + * Function - start or stop + * Handler_context - Address space handler context + * Returned context - context to be used with each call to the + * handler for this region + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ev_default_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context) +{ + ACPI_OBJECT_INTERNAL *region_obj = (ACPI_OBJECT_INTERNAL *) handle; + + + if (function == ACPI_REGION_DEACTIVATE) { + region_obj->region.region_flags &= ~(REGION_INITIALIZED); + *return_context = NULL; + } + else { + region_obj->region.region_flags |= REGION_INITIALIZED; + *return_context = handler_context; + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_initialize_region + * + * PARAMETERS: Region_obj - Region we are initializing + * + * RETURN: Status + * + * DESCRIPTION: Initializes the region, finds any _REG methods and saves them + * for execution at a later time + * + * Get the appropriate address space handler for a newly + * created region. + * + * This also performs address space specific intialization. For + * example, PCI regions must have an _ADR object that contains + * a PCI address in the scope of the defintion. This address is + * required to perform an access to PCI config space. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_initialize_region ( + ACPI_OBJECT_INTERNAL *region_obj, + u8 acpi_ns_locked) +{ + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_OBJECT_INTERNAL *obj_desc; + u32 space_id; + ACPI_NAMED_OBJECT *entry; /* Namespace Object */ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *reg_entry; + ACPI_NAME *reg_name_ptr = (ACPI_NAME *) METHOD_NAME__REG; + + + if (!region_obj) { + return (AE_BAD_PARAMETER); + } + + ACPI_ASSERT(region_obj->region.nte); + + entry = acpi_ns_get_parent_entry (region_obj->region.nte); + space_id = region_obj->region.space_id; + + region_obj->region.addr_handler = NULL; + region_obj->region.REGmethod = NULL; + region_obj->region.region_flags = INITIAL_REGION_FLAGS; + + /* + * Find any "_REG" associated with this region definition + */ + status = acpi_ns_search_one_scope (*reg_name_ptr, entry->child_table, + ACPI_TYPE_METHOD, ®_entry, NULL); + if (status == AE_OK) { + /* + * The _REG method is optional and there can be only one per region + * definition. This will be executed when the handler is attached + * or removed + */ + region_obj->region.REGmethod = reg_entry; + } + + /* + * The following loop depends upon the root nte having no parent + * ie: Acpi_gbl_Root_object->Parent_entry being set to NULL + */ + while (entry) { + /* + * Check to see if a handler exists + */ + handler_obj = NULL; + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) entry); + if (obj_desc) { + /* + * can only be a handler if the object exists + */ + switch (entry->type) + { + case ACPI_TYPE_DEVICE: + + handler_obj = obj_desc->device.addr_handler; + break; + + case ACPI_TYPE_PROCESSOR: + + handler_obj = obj_desc->processor.addr_handler; + break; + + case ACPI_TYPE_THERMAL: + + handler_obj = obj_desc->thermal_zone.addr_handler; + break; + } + + while (handler_obj) { + /* + * This guy has at least one address handler + * see if it has the type we want + */ + if (handler_obj->addr_handler.space_id == space_id) { + /* + * Found it! Now update the region and the handler + */ + acpi_ev_associate_region_and_handler(handler_obj, region_obj); + return (AE_OK); + } + + handler_obj = handler_obj->addr_handler.link; + + } /* while handlerobj */ + } + + /* + * This one does not have the handler we need + * Pop up one level + */ + entry = acpi_ns_get_parent_entry (entry); + + } /* while Entry != ROOT */ + + /* + * If we get here, there is no handler for this region + */ + return (AE_NOT_EXIST); +} + diff --git a/drivers/acpi/events/evsci.c b/drivers/acpi/events/evsci.c new file mode 100644 index 000000000..6bf605d85 --- /dev/null +++ b/drivers/acpi/events/evsci.c @@ -0,0 +1,302 @@ +/****************************************************************************** + * + * Module Name: evsci - System Control Interrupt configuration and + * legacy to ACPI mode state transition functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "namesp.h" +#include "hardware.h" +#include "events.h" + + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evsci"); + + +/* + * Elements correspond to counts for + * TMR, NOT_USED, GBL, PWR_BTN, SLP_BTN, RTC, + * and GENERAL respectively. These counts + * are modified by the ACPI interrupt handler... + * Note that GENERAL should probably be split out + * into one element for each bit in the GPE + * registers + */ + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_sci_handler + * + * PARAMETERS: none + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Interrupt handler that will figure out what function or + * control method to call to deal with a SCI. Installed + * using BU interrupt support. + * + ******************************************************************************/ + +u32 +acpi_ev_sci_handler (void *context) +{ + u32 interrupt_handled = INTERRUPT_NOT_HANDLED; + + /* + * ACPI Enabled? + * ------------- + * Make sure that ACPI is enabled by checking SCI_EN. Note that we are + * required to treat the SCI interrupt as sharable, level, active low. + */ + if (!acpi_hw_register_access (ACPI_READ, ACPI_MTX_DO_NOT_LOCK, (s32)SCI_EN)) { + REPORT_ERROR ("Received and SCI but ACPI is not enabled."); + return (INTERRUPT_NOT_HANDLED); + } + + /* + * Fixed Acpi_events: + * ------------- + * Check for and dispatch any Fixed Acpi_events that have occurred + */ + interrupt_handled |= acpi_ev_fixed_event_detect (); + + /* + * GPEs: + * ----- + * Check for and dispatch any GPEs that have occurred + */ + interrupt_handled |= acpi_ev_gpe_detect (); + + return (interrupt_handled); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_install_sci_handler + * + * PARAMETERS: none + * + * RETURN: Status + * + * DESCRIPTION: Installs SCI handler. + * + ******************************************************************************/ + +u32 +acpi_ev_install_sci_handler (void) +{ + u32 except = AE_OK; + + except = acpi_os_install_interrupt_handler ( + (u32) acpi_gbl_FACP->sci_int, + acpi_ev_sci_handler, + NULL); + + return (except); +} + + +/****************************************************************************** + + * + * FUNCTION: Acpi_ev_remove_sci_handler + * + * PARAMETERS: none + * + * RETURN: E_OK if handler uninstalled OK, E_ERROR if handler was not + * installed to begin with + * + * DESCRIPTION: Restores original status of all fixed event enable bits and + * removes SCI handler. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ev_remove_sci_handler (void) +{ +#if 0 + /* TBD:[Investigate] Figure this out!! Disable all events first ??? */ + + if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (TMR_FIXED_EVENT)) { + acpi_event_disable_event (TMR_FIXED_EVENT); + } + + if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (GBL_FIXED_EVENT)) { + acpi_event_disable_event (GBL_FIXED_EVENT); + } + + if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (PWR_BTN_FIXED_EVENT)) { + acpi_event_disable_event (PWR_BTN_FIXED_EVENT); + } + + if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (SLP_BTN_FIXED_EVENT)) { + acpi_event_disable_event (SLP_BTN_FIXED_EVENT); + } + + if (original_fixed_enable_bit_status ^ 1 << acpi_event_index (RTC_FIXED_EVENT)) { + acpi_event_disable_event (RTC_FIXED_EVENT); + } + + original_fixed_enable_bit_status = 0; + +#endif + + acpi_os_remove_interrupt_handler ( + (u32) acpi_gbl_FACP->sci_int, + acpi_ev_sci_handler); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_sci_count + * + * PARAMETERS: char * Event_name name (fully qualified name from namespace + * or one of the fixed event names defined above) + * of the event to check if it's generated an SCI. + * + * RETURN: Number of SCI's for requested event since last time i_sci_occured() + * was called for this event. + * + * DESCRIPTION: Checks to see if SCI has been generated from requested source + * since the last time this function was called. + * + ******************************************************************************/ + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_restore_acpi_state + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Restore the original ACPI state of the machine + * + ******************************************************************************/ + +void +acpi_ev_restore_acpi_state (void) +{ + s32 index; + + + /* Restore the state of the chipset enable bits. */ + + if (acpi_gbl_restore_acpi_chipset == TRUE) { + /* Restore the fixed events */ + + if (acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk + 2) != + acpi_gbl_pm1_enable_register_save) + { + acpi_os_out16 ((acpi_gbl_FACP->pm1a_evt_blk + 2), + acpi_gbl_pm1_enable_register_save); + } + + if (acpi_gbl_FACP->pm1b_evt_blk) { + if (acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk + 2) != + acpi_gbl_pm1_enable_register_save) + { + acpi_os_out16 ((acpi_gbl_FACP->pm1b_evt_blk + 2), + acpi_gbl_pm1_enable_register_save); + } + } + + + /* Ensure that all status bits are clear */ + + acpi_hw_clear_acpi_status (); + + + /* Now restore the GPEs */ + + for (index = 0; index < DIV_2 (acpi_gbl_FACP->gpe0blk_len); index++) { + if (acpi_os_in8 (acpi_gbl_FACP->gpe0blk + + DIV_2 (acpi_gbl_FACP->gpe0blk_len)) != + acpi_gbl_gpe0enable_register_save[index]) + { + acpi_os_out8 ((acpi_gbl_FACP->gpe0blk + + DIV_2 (acpi_gbl_FACP->gpe0blk_len)), + acpi_gbl_gpe0enable_register_save[index]); + } + } + + if (acpi_gbl_FACP->gpe1_blk && acpi_gbl_FACP->gpe1_blk_len) { + for (index = 0; index < DIV_2 (acpi_gbl_FACP->gpe1_blk_len); index++) { + if (acpi_os_in8 (acpi_gbl_FACP->gpe1_blk + + DIV_2 (acpi_gbl_FACP->gpe1_blk_len)) != + acpi_gbl_gpe1_enable_register_save[index]) + { + acpi_os_out8 ((acpi_gbl_FACP->gpe1_blk + + DIV_2 (acpi_gbl_FACP->gpe1_blk_len)), + acpi_gbl_gpe1_enable_register_save[index]); + } + } + } + + if (acpi_hw_get_mode() != acpi_gbl_original_mode) { + acpi_hw_set_mode (acpi_gbl_original_mode); + } + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ev_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free memory allocated for table storage. + * + ******************************************************************************/ + +void +acpi_ev_terminate (void) +{ + + /* + * Free global tables, etc. + */ + + if (acpi_gbl_gpe_registers) { + acpi_cm_free (acpi_gbl_gpe_registers); + } + + if (acpi_gbl_gpe_info) { + acpi_cm_free (acpi_gbl_gpe_info); + } + + return; +} + + diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c new file mode 100644 index 000000000..385365515 --- /dev/null +++ b/drivers/acpi/events/evxface.c @@ -0,0 +1,604 @@ +/****************************************************************************** + * + * Module Name: evxface - External interfaces for ACPI events + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" +#include "events.h" +#include "amlcode.h" +#include "interp.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evxface"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_install_fixed_event_handler + * + * PARAMETERS: Event - Event type to enable. + * Handler - Pointer to the handler function for the + * event + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function and then enables the + * event. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_install_fixed_event_handler ( + u32 event, + FIXED_EVENT_HANDLER handler, + void *context) +{ + ACPI_STATUS status = AE_OK; + + + /* Sanity check the parameters. */ + + if (event >= NUM_FIXED_EVENTS) { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_EVENTS); + + /* Don't allow two handlers. */ + + if (NULL != acpi_gbl_fixed_event_handlers[event].handler) { + status = AE_EXIST; + goto cleanup; + } + + + /* Install the handler before enabling the event - just in case... */ + + acpi_gbl_fixed_event_handlers[event].handler = handler; + acpi_gbl_fixed_event_handlers[event].context = context; + + if (1 != acpi_hw_register_access (ACPI_WRITE, + ACPI_MTX_LOCK, event + TMR_EN, 1)) + { + /* Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + + status = AE_ERROR; + goto cleanup; + } + + +cleanup: + acpi_cm_release_mutex (ACPI_MTX_EVENTS); + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_remove_fixed_event_handler + * + * PARAMETERS: Event - Event type to disable. + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Disables the event and unregisters the event handler. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_remove_fixed_event_handler ( + u32 event, + FIXED_EVENT_HANDLER handler) +{ + ACPI_STATUS status = AE_OK; + + + /* Sanity check the parameters. */ + + if (event >= NUM_FIXED_EVENTS) { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_EVENTS); + + /* Disable the event before removing the handler - just in case... */ + + if (0 != acpi_hw_register_access (ACPI_WRITE, + ACPI_MTX_LOCK, event + TMR_EN, 0)) + { + status = AE_ERROR; + goto cleanup; + } + + /* Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + +cleanup: + acpi_cm_release_mutex (ACPI_MTX_EVENTS); + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_install_notify_handler + * + * PARAMETERS: Device - The device for which notifies will be handled + * Handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: System_handler (00-7f) + * ACPI_DEVICE_NOTIFY: Driver_handler (80-ff) + * Handler - Address of the handler + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for notifies on an ACPI device + * + ******************************************************************************/ + +ACPI_STATUS +acpi_install_notify_handler ( + ACPI_HANDLE device, + u32 handler_type, + NOTIFY_HANDLER handler, + void *context) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *notify_obj; + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status = AE_OK; + + + /* Parameter validation */ + + if ((!handler) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) + { + return (AE_BAD_PARAMETER); + } + + /* Convert and validate the device handle */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + obj_entry = acpi_ns_convert_handle_to_entry (device); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + + /* + * Support for global notify handlers. These handlers are invoked for + * every notifiy of the type specifiec + */ + + if (device == ACPI_ROOT_OBJECT) { + /* + * Make sure the handler is not already installed. + */ + + if (((handler_type == ACPI_SYSTEM_NOTIFY) && + acpi_gbl_sys_notify.handler) || + ((handler_type == ACPI_DEVICE_NOTIFY) && + acpi_gbl_drv_notify.handler)) + { + status = AE_EXIST; + goto unlock_and_exit; + } + + if (handler_type == ACPI_SYSTEM_NOTIFY) { + acpi_gbl_sys_notify.nte = obj_entry; + acpi_gbl_sys_notify.handler = handler; + acpi_gbl_sys_notify.context = context; + } + + else { + acpi_gbl_drv_notify.nte = obj_entry; + acpi_gbl_drv_notify.handler = handler; + acpi_gbl_drv_notify.context = context; + } + + + /* Global notify handler installed */ + + goto unlock_and_exit; + } + + + /* + * These are the ONLY objects that can receive ACPI notifications + */ + + if ((obj_entry->type != ACPI_TYPE_DEVICE) && + (obj_entry->type != ACPI_TYPE_PROCESSOR) && + (obj_entry->type != ACPI_TYPE_POWER) && + (obj_entry->type != ACPI_TYPE_THERMAL)) + { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) obj_entry); + if (obj_desc) { + /* + * The object exists. + * Make sure the handler is not already installed. + */ + + if (((handler_type == ACPI_SYSTEM_NOTIFY) && + obj_desc->device.sys_handler) || + ((handler_type == ACPI_DEVICE_NOTIFY) && + obj_desc->device.drv_handler)) + { + status = AE_EXIST; + goto unlock_and_exit; + } + } + + else { + /* Create a new object */ + + obj_desc = acpi_cm_create_internal_object (obj_entry->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Attach new object to the NTE */ + + status = acpi_ns_attach_object (device, obj_desc, (u8) obj_entry->type); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + + /* + * If we get here, we know that there is no handler installed + * so let's party + */ + notify_obj = acpi_cm_create_internal_object (INTERNAL_TYPE_NOTIFY); + if (!notify_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + notify_obj->notify_handler.nte = obj_entry; + notify_obj->notify_handler.handler = handler; + notify_obj->notify_handler.context = context; + + + if (handler_type == ACPI_SYSTEM_NOTIFY) { + obj_desc->device.sys_handler = notify_obj; + } + + else { + obj_desc->device.drv_handler = notify_obj; + } + + +unlock_and_exit: + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_remove_notify_handler + * + * PARAMETERS: Device - The device for which notifies will be handled + * Handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: System_handler (00-7f) + * ACPI_DEVICE_NOTIFY: Driver_handler (80-ff) + * Handler - Address of the handler + * RETURN: Status + * + * DESCRIPTION: Remove a handler for notifies on an ACPI device + * + ******************************************************************************/ + +ACPI_STATUS +acpi_remove_notify_handler ( + ACPI_HANDLE device, + u32 handler_type, + NOTIFY_HANDLER handler) +{ + ACPI_OBJECT_INTERNAL *notify_obj; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status = AE_OK; + + + /* Parameter validation */ + + if ((!handler) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) + { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Convert and validate the device handle */ + + obj_entry = acpi_ns_convert_handle_to_entry (device); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * These are the ONLY objects that can receive ACPI notifications + */ + + if ((obj_entry->type != ACPI_TYPE_DEVICE) && + (obj_entry->type != ACPI_TYPE_PROCESSOR) && + (obj_entry->type != ACPI_TYPE_POWER) && + (obj_entry->type != ACPI_TYPE_THERMAL)) + { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) obj_entry); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* + * The object exists. + * + * Make sure the handler is installed. + */ + + if (handler_type == ACPI_SYSTEM_NOTIFY) { + notify_obj = obj_desc->device.sys_handler; + } + else { + notify_obj = obj_desc->device.drv_handler; + } + + if ((!notify_obj) || + (notify_obj->notify_handler.handler != handler)) + { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * Now we can remove the handler + */ + if (handler_type == ACPI_SYSTEM_NOTIFY) { + obj_desc->device.sys_handler = NULL; + } + else { + obj_desc->device.drv_handler = NULL; + } + + acpi_cm_remove_reference (notify_obj); + +unlock_and_exit: + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + +/****************************************************************************** + * + * FUNCTION: Acpi_install_gpe_handler + * + * PARAMETERS: Gpe_number - The GPE number. The numbering scheme is + * bank 0 first, then bank 1. + * Trigger - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * Handler - Address of the handler + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Acpi_event. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_install_gpe_handler ( + u32 gpe_number, + u32 type, + GPE_HANDLER handler, + void *context) +{ + ACPI_STATUS status = AE_OK; + + /* Parameter validation */ + + if (!handler || (gpe_number > NUM_GPE)) { + return (AE_BAD_PARAMETER); + } + + /* Ensure that we have a valid GPE number */ + + if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_EVENTS); + + /* Make sure that there isn't a handler there already */ + + if (acpi_gbl_gpe_info[gpe_number].handler) { + status = AE_EXIST; + goto cleanup; + } + + /* Install the handler */ + + acpi_gbl_gpe_info[gpe_number].handler = handler; + acpi_gbl_gpe_info[gpe_number].context = context; + acpi_gbl_gpe_info[gpe_number].type = (u8) type; + + /* Clear the GPE (of stale events), the enable it */ + + acpi_hw_clear_gpe (gpe_number); + acpi_hw_enable_gpe (gpe_number); + +cleanup: + acpi_cm_release_mutex (ACPI_MTX_EVENTS); + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_remove_gpe_handler + * + * PARAMETERS: Gpe_number - The event to remove a handler + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for a General Purpose Acpi_event. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_remove_gpe_handler ( + u32 gpe_number, + GPE_HANDLER handler) +{ + ACPI_STATUS status = AE_OK; + + + /* Parameter validation */ + + if (!handler || (gpe_number > NUM_GPE)) { + return (AE_BAD_PARAMETER); + } + + /* Ensure that we have a valid GPE number */ + + if (acpi_gbl_gpe_valid[gpe_number] == ACPI_GPE_INVALID) { + return (AE_BAD_PARAMETER); + } + + /* Disable the GPE before removing the handler */ + + acpi_hw_disable_gpe (gpe_number); + + acpi_cm_acquire_mutex (ACPI_MTX_EVENTS); + + /* Make sure that the installed handler is the same */ + + if (acpi_gbl_gpe_info[gpe_number].handler != handler) { + acpi_hw_enable_gpe (gpe_number); + status = AE_BAD_PARAMETER; + goto cleanup; + } + + /* Remove the handler */ + + acpi_gbl_gpe_info[gpe_number].handler = NULL; + acpi_gbl_gpe_info[gpe_number].context = NULL; + +cleanup: + acpi_cm_release_mutex (ACPI_MTX_EVENTS); + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_acquire_global_lock + * + * PARAMETERS: Timeout - How long the caller is willing to wait + * Out_handle - A handle to the lock if acquired + * + * RETURN: Status + * + * DESCRIPTION: Acquire the ACPI Global Lock + * + ******************************************************************************/ + +ACPI_STATUS +acpi_acquire_global_lock ( + u32 timeout, + u32 *out_handle) +{ + ACPI_STATUS status; + + + acpi_aml_enter_interpreter (); + + /* + * TBD: [Restructure] add timeout param to internal interface, and + * perhaps INTERPRETER_LOCKED + */ + + status = acpi_ev_acquire_global_lock (); + acpi_aml_exit_interpreter (); + + *out_handle = 0; + return status; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_release_global_lock + * + * PARAMETERS: Handle - Returned from Acpi_acquire_global_lock + * + * RETURN: Status + * + * DESCRIPTION: Release the ACPI Global Lock + * + ******************************************************************************/ + +ACPI_STATUS +acpi_release_global_lock ( + u32 handle) +{ + + + /* TBD: [Restructure] Validate handle */ + + acpi_ev_release_global_lock (); + return AE_OK; +} + + diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c new file mode 100644 index 000000000..01f95f0c0 --- /dev/null +++ b/drivers/acpi/events/evxfevnt.c @@ -0,0 +1,513 @@ +/****************************************************************************** + * + * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" +#include "events.h" +#include "amlcode.h" +#include "interp.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evxfevnt"); + + +/************************************************************************** + * + * FUNCTION: Acpi_enable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Ensures that the system control interrupt (SCI) is properly + * configured, disables SCI event sources, installs the SCI + * handler, and transfers the system into ACPI mode. + * + *************************************************************************/ + +ACPI_STATUS +acpi_enable (void) +{ + ACPI_STATUS status; + + + /* Make sure we've got ACPI tables */ + + if (!acpi_gbl_DSDT) { + return (AE_NO_ACPI_TABLES); + } + + /* Make sure the BIOS supports ACPI mode */ + + if (SYS_MODE_LEGACY == acpi_hw_get_mode_capabilities()) { + return (AE_ERROR); + } + + acpi_gbl_original_mode = acpi_hw_get_mode(); + + /* + * Initialize the Fixed and General Purpose Acpi_events prior. This is + * done prior to enabling SCIs to prevent interrupts from occuring + * before handers are installed. + */ + + if (ACPI_FAILURE (acpi_ev_fixed_event_initialize ())) { + return (AE_ERROR); + } + + if (ACPI_FAILURE (acpi_ev_gpe_initialize())) { + return (AE_ERROR); + } + + /* Install the SCI handler */ + + if (ACPI_FAILURE (acpi_ev_install_sci_handler ())) { + return (AE_ERROR); + } + + /* Transition to ACPI mode */ + + if (AE_OK != acpi_hw_set_mode (SYS_MODE_ACPI)) { + return (AE_ERROR); + } + + /* Install handlers for control method GPE handlers (_Lxx, _Exx) */ + + acpi_ev_init_gpe_control_methods (); + + status = acpi_ev_init_global_lock_handler (); + + return (status); +} + + +/************************************************************************** + * + * FUNCTION: Acpi_disable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Returns the system to original ACPI/legacy mode, and + * uninstalls the SCI interrupt handler. + * + *************************************************************************/ + +ACPI_STATUS +acpi_disable (void) +{ + + + /* Restore original mode */ + + if (AE_OK != acpi_hw_set_mode (acpi_gbl_original_mode)) { + return (AE_ERROR); + } + + /* Unload the SCI interrupt handler */ + + acpi_ev_remove_sci_handler (); + acpi_ev_restore_acpi_state (); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_enable_event + * + * PARAMETERS: Event - The fixed event or GPE to be enabled + * Type - The type of event + * + * RETURN: Status + * + * DESCRIPTION: Enable an ACPI event (fixed and general purpose) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_enable_event ( + u32 event, + u32 type) +{ + ACPI_STATUS status = AE_OK; + u32 register_id; + + + /* The Type must be either Fixed Acpi_event or GPE */ + + switch (type) + { + + case ACPI_EVENT_FIXED: + + /* Decode the Fixed Acpi_event */ + + switch (event) + { + case ACPI_EVENT_PMTIMER: + register_id = TMR_EN; + break; + + case ACPI_EVENT_GLOBAL: + register_id = GBL_EN; + break; + + case ACPI_EVENT_POWER_BUTTON: + register_id = PWRBTN_EN; + break; + + case ACPI_EVENT_SLEEP_BUTTON: + register_id = SLPBTN_EN; + break; + + case ACPI_EVENT_RTC: + register_id = RTC_EN; + break; + + default: + return (AE_BAD_PARAMETER); + break; + } + + /* + * Enable the requested fixed event (by writing a one to the + * enable register bit) + */ + + acpi_hw_register_access (ACPI_WRITE, TRUE, register_id, 1); + break; + + + case ACPI_EVENT_GPE: + + /* Ensure that we have a valid GPE number */ + + if ((event >= NUM_GPE) || + (acpi_gbl_gpe_valid[event] == ACPI_GPE_INVALID)) + { + return (AE_BAD_PARAMETER); + } + + + /* Enable the requested GPE number */ + + acpi_hw_enable_gpe (event); + break; + + + default: + + status = AE_BAD_PARAMETER; + } + + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_disable_event + * + * PARAMETERS: Event - The fixed event or GPE to be enabled + * Type - The type of event + * + * RETURN: Status + * + * DESCRIPTION: Disable an ACPI event (fixed and general purpose) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_disable_event ( + u32 event, + u32 type) +{ + ACPI_STATUS status = AE_OK; + u32 register_id; + + + /* The Type must be either Fixed Acpi_event or GPE */ + + switch (type) + { + + case ACPI_EVENT_FIXED: + + /* Decode the Fixed Acpi_event */ + + switch (event) + { + case ACPI_EVENT_PMTIMER: + register_id = TMR_EN; + break; + + case ACPI_EVENT_GLOBAL: + register_id = GBL_EN; + break; + + case ACPI_EVENT_POWER_BUTTON: + register_id = PWRBTN_EN; + break; + + case ACPI_EVENT_SLEEP_BUTTON: + register_id = SLPBTN_EN; + break; + + case ACPI_EVENT_RTC: + register_id = RTC_EN; + break; + + default: + return (AE_BAD_PARAMETER); + break; + } + + /* + * Disable the requested fixed event (by writing a zero to the + * enable register bit) + */ + + acpi_hw_register_access (ACPI_WRITE, TRUE, register_id, 0); + break; + + + case ACPI_EVENT_GPE: + + /* Ensure that we have a valid GPE number */ + + if ((event >= NUM_GPE) || + (acpi_gbl_gpe_valid[event] == ACPI_GPE_INVALID)) + { + return (AE_BAD_PARAMETER); + } + + /* Disable the requested GPE number */ + + acpi_hw_disable_gpe (event); + break; + + + default: + status = AE_BAD_PARAMETER; + } + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_clear_event + * + * PARAMETERS: Event - The fixed event or GPE to be cleared + * Type - The type of event + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (fixed and general purpose) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_clear_event ( + u32 event, + u32 type) +{ + ACPI_STATUS status = AE_OK; + u32 register_id; + + + /* The Type must be either Fixed Acpi_event or GPE */ + + switch (type) + { + + case ACPI_EVENT_FIXED: + + /* Decode the Fixed Acpi_event */ + + switch (event) + { + case ACPI_EVENT_PMTIMER: + register_id = TMR_STS; + break; + + case ACPI_EVENT_GLOBAL: + register_id = GBL_STS; + break; + + case ACPI_EVENT_POWER_BUTTON: + register_id = PWRBTN_STS; + break; + + case ACPI_EVENT_SLEEP_BUTTON: + register_id = SLPBTN_STS; + break; + + case ACPI_EVENT_RTC: + register_id = RTC_STS; + break; + + default: + return (AE_BAD_PARAMETER); + break; + } + + /* + * Clear the requested fixed event (By writing a one to the + * status register bit) + */ + + acpi_hw_register_access (ACPI_WRITE, TRUE, register_id, 1); + break; + + + case ACPI_EVENT_GPE: + + /* Ensure that we have a valid GPE number */ + + if ((event >= NUM_GPE) || + (acpi_gbl_gpe_valid[event] == ACPI_GPE_INVALID)) + { + return (AE_BAD_PARAMETER); + } + + + acpi_hw_clear_gpe (event); + break; + + + default: + + status = AE_BAD_PARAMETER; + } + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_event_status + * + * PARAMETERS: Event - The fixed event or GPE + * Type - The type of event + * Status - Where the current status of the event will + * be returned + * + * RETURN: Status + * + * DESCRIPTION: Obtains and returns the current status of the event + * + ******************************************************************************/ + + +ACPI_STATUS +acpi_get_event_status ( + u32 event, + u32 type, + ACPI_EVENT_STATUS *event_status) +{ + ACPI_STATUS status = AE_OK; + u32 register_id; + + + if (!event_status) { + return (AE_BAD_PARAMETER); + } + + + /* The Type must be either Fixed Acpi_event or GPE */ + + switch (type) + { + + case ACPI_EVENT_FIXED: + + /* Decode the Fixed Acpi_event */ + + switch (event) + { + case ACPI_EVENT_PMTIMER: + register_id = TMR_STS; + break; + + case ACPI_EVENT_GLOBAL: + register_id = GBL_STS; + break; + + case ACPI_EVENT_POWER_BUTTON: + register_id = PWRBTN_STS; + break; + + case ACPI_EVENT_SLEEP_BUTTON: + register_id = SLPBTN_STS; + break; + + case ACPI_EVENT_RTC: + register_id = RTC_STS; + break; + + default: + return (AE_BAD_PARAMETER); + break; + } + + /* Get the status of the requested fixed event */ + + *event_status = acpi_hw_register_access (ACPI_READ, TRUE, register_id); + break; + + + case ACPI_EVENT_GPE: + + /* Ensure that we have a valid GPE number */ + + if ((event >= NUM_GPE) || + (acpi_gbl_gpe_valid[event] == ACPI_GPE_INVALID)) + { + return (AE_BAD_PARAMETER); + } + + + /* Obtain status on the requested GPE number */ + + acpi_hw_get_gpe_status (event, event_status); + break; + + + default: + status = AE_BAD_PARAMETER; + } + + + return (status); +} + diff --git a/drivers/acpi/events/evxfregn.c b/drivers/acpi/events/evxfregn.c new file mode 100644 index 000000000..c54c726e8 --- /dev/null +++ b/drivers/acpi/events/evxfregn.c @@ -0,0 +1,389 @@ +/****************************************************************************** + * + * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and + * Address Spaces. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" +#include "events.h" +#include "amlcode.h" +#include "interp.h" + +#define _COMPONENT EVENT_HANDLING + MODULE_NAME ("evxfregn"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_install_address_space_handler + * + * PARAMETERS: Device - Handle for the device + * Space_id - The address space ID + * Handler - Address of the handler + * Context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for accesses on an address space controlled + * a specific device. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_install_address_space_handler ( + ACPI_HANDLE device, + ACPI_ADDRESS_SPACE_TYPE space_id, + ADDRESS_SPACE_HANDLER handler, + ADDRESS_SPACE_SETUP setup, + void *context) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status = AE_OK; + OBJECT_TYPE_INTERNAL type; + u16 flags = 0; + + + /* Parameter validation */ + + if ((!device) || + ((!handler) && (handler != ACPI_DEFAULT_HANDLER)) || + (space_id > ACPI_MAX_ADDRESS_SPACE)) + { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Convert and validate the device handle */ + + obj_entry = acpi_ns_convert_handle_to_entry (device); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * This registration is valid for only the types below + * and the root. This is where the default handlers + * get placed. + */ + + if ((obj_entry->type != ACPI_TYPE_DEVICE) && + (obj_entry->type != ACPI_TYPE_PROCESSOR) && + (obj_entry->type != ACPI_TYPE_THERMAL) && + (obj_entry != acpi_gbl_root_object)) + { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if (handler == ACPI_DEFAULT_HANDLER) { + flags = ADDR_HANDLER_DEFAULT_INSTALLED; + + switch (space_id) + { + case ADDRESS_SPACE_SYSTEM_MEMORY: + handler = acpi_aml_system_memory_space_handler; + setup = acpi_ev_system_memory_region_setup; + break; + + case ADDRESS_SPACE_SYSTEM_IO: + handler = acpi_aml_system_io_space_handler; + setup = acpi_ev_io_space_region_setup; + break; + + case ADDRESS_SPACE_PCI_CONFIG: + handler = acpi_aml_pci_config_space_handler; + setup = acpi_ev_pci_config_region_setup; + break; + + default: + status = AE_NOT_EXIST; + goto unlock_and_exit; + break; + } + } + + /* + * If the caller hasn't specified a setup routine, use the default + */ + if (!setup) { + setup = acpi_ev_default_region_setup; + } + + /* + * Check for an existing internal object + */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) obj_entry); + if (obj_desc) { + /* + * The object exists. + * Make sure the handler is not already installed. + */ + + /* check the address handler the user requested */ + + handler_obj = obj_desc->device.addr_handler; + while (handler_obj) { + /* + * We have an Address handler, see if user requested this + * address space. + */ + if(handler_obj->addr_handler.space_id == space_id) { + status = AE_EXIST; + goto unlock_and_exit; + } + + /* + * Move through the linked list of handlers + */ + handler_obj = handler_obj->addr_handler.link; + } + } + + else { + /* Obj_desc does not exist, create one */ + + if (obj_entry->type == ACPI_TYPE_ANY) { + type = ACPI_TYPE_DEVICE; + } + + else { + type = obj_entry->type; + } + + obj_desc = acpi_cm_create_internal_object (type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init new descriptor */ + + obj_desc->common.type = (u8) type; + + /* Attach the new object to the NTE */ + + status = acpi_ns_attach_object (device, obj_desc, (u8) type); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + goto unlock_and_exit; + } + + /* TBD: [Investigate] Will this always be of type DEVICE? */ + + if (type == ACPI_TYPE_DEVICE) { + obj_desc->device.handle = device; + } + } + + /* + * Now we can install the handler + * + * At this point we know that there is no existing handler. + * So, we just allocate the object for the handler and link it + * into the list. + */ + handler_obj = acpi_cm_create_internal_object (INTERNAL_TYPE_ADDRESS_HANDLER); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + handler_obj->addr_handler.space_id = (u16) space_id; + handler_obj->addr_handler.hflags = flags; + handler_obj->addr_handler.link = obj_desc->device.addr_handler; + handler_obj->addr_handler.region_list = NULL; + handler_obj->addr_handler.nte = obj_entry; + handler_obj->addr_handler.handler = handler; + handler_obj->addr_handler.context = context; + handler_obj->addr_handler.setup = setup; + + /* + * Now walk the namespace finding all of the regions this + * handler will manage. + * + * We start at the device and search the branch toward + * the leaf nodes until either the leaf is encountered or + * a device is detected that has an address handler of the + * same type. + * + * In either case we back up and search down the remainder + * of the branch + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, device, + ACPI_INT32_MAX, NS_WALK_NO_UNLOCK, + acpi_ev_addr_handler_helper, + handler_obj, NULL); + + /* + * Place this handler 1st on the list + */ + + handler_obj->common.reference_count = + (u16) (handler_obj->common.reference_count + + obj_desc->common.reference_count - 1); + obj_desc->device.addr_handler = handler_obj; + + +unlock_and_exit: + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_remove_address_space_handler + * + * PARAMETERS: Space_id - The address space ID + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for accesses on an Operation Region + * + ******************************************************************************/ + +ACPI_STATUS +acpi_remove_address_space_handler ( + ACPI_HANDLE device, + ACPI_ADDRESS_SPACE_TYPE space_id, + ADDRESS_SPACE_HANDLER handler) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *handler_obj; + ACPI_OBJECT_INTERNAL *region_obj; + ACPI_OBJECT_INTERNAL **last_obj_ptr; + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status = AE_OK; + + + /* Parameter validation */ + + if ((!device) || + ((!handler) && (handler != ACPI_DEFAULT_HANDLER)) || + (space_id > ACPI_MAX_ADDRESS_SPACE)) + { + return (AE_BAD_PARAMETER); + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Convert and validate the device handle */ + + obj_entry = acpi_ns_convert_handle_to_entry (device); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + + /* Make sure the internal object exists */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) obj_entry); + if (!obj_desc) { + /* + * The object DNE. + */ + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* + * find the address handler the user requested + */ + + handler_obj = obj_desc->device.addr_handler; + last_obj_ptr = &obj_desc->device.addr_handler; + while (handler_obj) { + /* + * We have a handler, see if user requested this one + */ + + if(handler_obj->addr_handler.space_id == space_id) { + /* + * Got it, first dereference this in the Regions + */ + region_obj = handler_obj->addr_handler.region_list; + + /* Walk the handler's region list */ + + while (region_obj) { + /* + * First disassociate the handler from the region. + * + * NOTE: this doesn't mean that the region goes away + * The region is just inaccessible as indicated to + * the _REG method + */ + acpi_ev_disassociate_region_from_handler(region_obj); + + /* + * Walk the list, since we took the first region and it + * was removed from the list by the dissassociate call + * we just get the first item on the list again + */ + region_obj = handler_obj->addr_handler.region_list; + + } + + /* + * Remove this Handler object from the list + */ + *last_obj_ptr = handler_obj->addr_handler.link; + + /* + * Now we can delete the handler object + */ + acpi_cm_remove_reference (handler_obj); + acpi_cm_remove_reference (handler_obj); + + goto unlock_and_exit; + } + + /* + * Move through the linked list of handlers + */ + last_obj_ptr = &handler_obj->addr_handler.link; + handler_obj = handler_obj->addr_handler.link; + } + + + /* + * The handler does not exist + */ + status = AE_NOT_EXIST; + + +unlock_and_exit: + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + diff --git a/drivers/acpi/hardware/hwacpi.c b/drivers/acpi/hardware/hwacpi.c new file mode 100644 index 000000000..22c5471d8 --- /dev/null +++ b/drivers/acpi/hardware/hwacpi.c @@ -0,0 +1,207 @@ + +/****************************************************************************** + * + * Module Name: hwacpi - ACPI hardware functions - mode and timer + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" + + +#define _COMPONENT HARDWARE + MODULE_NAME ("hwacpi"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_set_mode + * + * PARAMETERS: Mode - SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * RETURN: Status + * + * DESCRIPTION: Transitions the system into the requested mode or does nothing + * if the system is already in that mode. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_hw_set_mode ( + u32 mode) +{ + + ACPI_STATUS status = AE_ERROR; + + + if (mode == SYS_MODE_ACPI) { + /* BIOS should have disabled ALL fixed and GP events */ + + acpi_os_out8 (acpi_gbl_FACP->smi_cmd, acpi_gbl_FACP->acpi_enable); + } + + else if (mode == SYS_MODE_LEGACY) { + /* + * BIOS should clear all fixed status bits and restore fixed event + * enable bits to default + */ + + acpi_os_out8 (acpi_gbl_FACP->smi_cmd, acpi_gbl_FACP->acpi_disable); + } + + if (acpi_hw_get_mode () == mode) { + status = AE_OK; + } + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw + + * + * PARAMETERS: none + * + * RETURN: SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * DESCRIPTION: Return current operating state of system. Determined by + * querying the SCI_EN bit. + * + ******************************************************************************/ + +u32 +acpi_hw_get_mode (void) +{ + + + if (acpi_hw_register_access (ACPI_READ, ACPI_MTX_LOCK, (s32)SCI_EN)) { + return (SYS_MODE_ACPI); + } + else { + return (SYS_MODE_LEGACY); + } +} + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_get_mode_capabilities + * + * PARAMETERS: none + * + * RETURN: logical OR of SYS_MODE_ACPI and SYS_MODE_LEGACY determined at initial + * system state. + * + * DESCRIPTION: Returns capablities of system + * + ******************************************************************************/ + +u32 +acpi_hw_get_mode_capabilities (void) +{ + + + if (!(acpi_gbl_system_flags & SYS_MODES_MASK)) { + if (acpi_hw_get_mode () == SYS_MODE_LEGACY) { + /* + * Assume that if this call is being made, Acpi_init has been called + * and ACPI support has been established by the presence of the + * tables. Therefore since we're in SYS_MODE_LEGACY, the system + * must support both modes + */ + + acpi_gbl_system_flags |= (SYS_MODE_ACPI | SYS_MODE_LEGACY); + } + + else { + /* TBD: [Investigate] !!! this may be unsafe... */ + /* + * system is is ACPI mode, so try to switch back to LEGACY to see if + * it is supported + */ + acpi_hw_set_mode (SYS_MODE_LEGACY); + + if (acpi_hw_get_mode () == SYS_MODE_LEGACY) { + /* Now in SYS_MODE_LEGACY, so both are supported */ + + acpi_gbl_system_flags |= (SYS_MODE_ACPI | SYS_MODE_LEGACY); + acpi_hw_set_mode (SYS_MODE_ACPI); + } + + else { + /* Still in SYS_MODE_ACPI so this must be an ACPI only system */ + + acpi_gbl_system_flags |= SYS_MODE_ACPI; + } + } + } + + return (acpi_gbl_system_flags & SYS_MODES_MASK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_pmt_ticks + * + * PARAMETERS: none + * + * RETURN: Current value of the ACPI PMT (timer) + * + * DESCRIPTION: Obtains current value of ACPI PMT + * + ******************************************************************************/ + +u32 +acpi_hw_pmt_ticks (void) +{ + u32 ticks; + + ticks = acpi_os_in32 (acpi_gbl_FACP->pm_tmr_blk); + + return (ticks); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_pmt_resolution + * + * PARAMETERS: none + * + * RETURN: Number of bits of resolution in the PMT (either 24 or 32) + * + * DESCRIPTION: Obtains resolution of the ACPI PMT (either 24bit or 32bit) + * + ******************************************************************************/ + +u32 +acpi_hw_pmt_resolution (void) +{ + if (0 == acpi_gbl_FACP->tmr_val_ext) { + return (24); + } + + return (32); +} + diff --git a/drivers/acpi/hardware/hwcpu32.c b/drivers/acpi/hardware/hwcpu32.c new file mode 100644 index 000000000..46cff8a66 --- /dev/null +++ b/drivers/acpi/hardware/hwcpu32.c @@ -0,0 +1,716 @@ +/****************************************************************************** + * + * Name: hwcpu32.c - CPU support for IA32 (Throttling, Cx_states) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "namesp.h" +#include "hardware.h" + +#define _COMPONENT HARDWARE + MODULE_NAME ("Hwcpu32"); + + +#define BIT_4 0x10 /* TBD: [investigate] is this correct? */ + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_enter_c1 + * + * PARAMETERS: Pblk_address - Address of the processor control block + * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep + * + * RETURN: Function status. + * + * DESCRIPTION: Set C1 state on IA32 processor (halt) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_enter_c1( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks) +{ + u32 timer = 0; + + + if (!pm_timer_ticks) { + /* + * Enter C1: + * --------- + */ + enable(); + halt(); + *pm_timer_ticks = ACPI_UINT32_MAX; + } + else { + timer = acpi_hw_pmt_ticks (); + + /* + * Enter C1: + * --------- + */ + enable (); + halt (); + + /* + * Compute Time in C1: + * ------------------- + */ + timer = acpi_hw_pmt_ticks () - timer; + + *pm_timer_ticks = timer; + } + + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_enter_c2 + * + * PARAMETERS: Pblk_address - Address of the processor control block + * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep + * + * RETURN: <none> + * + * DESCRIPTION: Set C2 state on IA32 processor + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_enter_c2( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks) +{ + u32 timer = 0; + + + if (!pblk_address || !pm_timer_ticks) { + return AE_BAD_PARAMETER; + } + + /* + * Disable interrupts before all C2/C3 transitions. + */ + disable (); + + timer = acpi_hw_pmt_ticks (); + + /* + * Enter C2: + * --------- + * Read from the P_LVL2 (P_BLK+4) register to invoke a C2 transition. + */ + acpi_os_in8 ((ACPI_IO_ADDRESS) (pblk_address + 4)); + + /* + * Perform Dummy Op: + * ----------------- + * We have to do something useless after reading LVL2 because chipsets + * cannot guarantee that STPCLK# gets asserted in time to freeze execution. + */ + acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk); + + /* + * Compute Time in C2: + * ------------------- + */ + timer = acpi_hw_pmt_ticks () - timer; + + *pm_timer_ticks = timer; + + /* + * Re-enable interrupts after coming out of C2/C3. + */ + enable (); + + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_enter_c3 + * + * PARAMETERS: Pblk_address - Address of the processor control block + * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep + * + * RETURN: Status of function + * + * DESCRIPTION: Set C3 state on IA32 processor (UP only, cache coherency via + * disabling bus mastering) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_enter_c3( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks) +{ + u32 timer = 0; + u8 pm2_cnt_blk = 0; + u32 bus_master_status = 0; + + + if (!pblk_address || !pm_timer_ticks) { + return AE_BAD_PARAMETER; + } + + /* + * Check the BM_STS bit, if it is set, do not enter C3 + * but clear the bit (with a write) and exit, telling + * the calling module that we spent zero time in C3. + * If bus mastering continues, this action should + * eventually cause a demotion to C2 + */ + if (1 == (bus_master_status = + acpi_hw_register_access (ACPI_READ, ACPI_MTX_LOCK, (s32)BM_STS))) + { + /* + * Clear the BM_STS bit by setting it. + */ + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_STS, 1); + *pm_timer_ticks = 0; + return AE_OK; + } + + /* + * Disable interrupts before all C2/C3 transitions. + */ + disable(); + + /* + * Disable Bus Mastering: + * ---------------------- + * Set the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits. + */ + pm2_cnt_blk = acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk); + pm2_cnt_blk |= 0x01; + acpi_os_out8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk, pm2_cnt_blk); + + /* + * Get the timer base before entering C state + */ + timer = acpi_hw_pmt_ticks (); + + /* + * Enter C3: + * --------- + * Read from the P_LVL3 (P_BLK+5) register to invoke a C3 transition. + */ + acpi_os_in8 ((ACPI_IO_ADDRESS)(pblk_address + 5)); + + /* + * Perform Dummy Op: + * ----------------- + * We have to do something useless after reading LVL3 because chipsets + * cannot guarantee that STPCLK# gets asserted in time to freeze execution. + */ + acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk); + + /* + * Immediately compute the time in the C state + */ + timer = acpi_hw_pmt_ticks() - timer; + + /* + * Re-Enable Bus Mastering: + * ------------------------ + * Clear the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits. + */ + pm2_cnt_blk = acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk); + pm2_cnt_blk &= 0xFE; + acpi_os_out8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk, pm2_cnt_blk); + + /* TBD: [Unhandled]: Support 24-bit timers (this algorithm assumes 32-bit) */ + + *pm_timer_ticks = timer; + + /* + * Re-enable interrupts after coming out of C2/C3. + */ + enable(); + + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_enter_cx + * + * PARAMETERS: Processor_handle - handle of the processor + * + * RETURN: Status of function + * + * DESCRIPTION: Invoke the currently active processor Cx handler to put this + * processor to sleep. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_enter_cx ( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks) +{ + + if (!acpi_hw_cx_handlers[acpi_hw_active_cx_state]) { + return AE_SUPPORT; + } + + return (acpi_hw_cx_handlers[acpi_hw_active_cx_state] (pblk_address, pm_timer_ticks)); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_set_cx + * + * PARAMETERS: State - value (1-3) of the Cx state to 'make active' + * + * RETURN: Function status. + * + * DESCRIPTION: Sets the state to use during calls to Acpi_hw_enter_cx(). + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_set_cx ( + u32 cx_state) +{ + /* + * Supported State? + * ---------------- + */ + if ((cx_state < 1) || (cx_state > 3)) { + return AE_BAD_PARAMETER; + } + + if (!acpi_hw_cx_handlers[cx_state]) { + return AE_SUPPORT; + } + + /* + * New Cx State? + * ------------- + * We only care when moving from one state to another... + */ + if (acpi_hw_active_cx_state == cx_state) { + return AE_OK; + } + + /* + * Prepare to Use New State: + * ------------------------- + * If the new Cx_state is C3, the BM_RLD bit must be set to allow + * the generation of a bus master requets to cause the processor + * in the C3 state to transition to the C0 state. + */ + switch (cx_state) + { + case 3: + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_RLD, 1); + break; + } + + /* + * Clean up from Old State: + * ------------------------ + * If the old Cx_state was C3, the BM_RLD bit is reset. When the + * bit is reset, the generation of a bus master request does not + * effect any processor in the C3 state. + */ + switch (acpi_hw_active_cx_state) + { + case 3: + acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_RLD, 0); + break; + } + + /* + * Enable: + * ------- + */ + acpi_hw_active_cx_state = cx_state; + + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_get_cx_info + * + * PARAMETERS: Cx_states - Information (latencies) on all Cx states + * + * RETURN: Status of function + * + * DESCRIPTION: This function is called both to initialize Cx handling + * and retrieve the current Cx information (latency values). + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_get_cx_info ( + u32 cx_states[]) +{ + u8 SMP_system = FALSE; + + + if (!cx_states) { + return(AE_BAD_PARAMETER); + } + + /* + * TBD: [Unhandled] need to init SMP_system using info from the MAPIC + * table. + */ + + /* + * Set Defaults: + * ------------- + * C0 and C1 support is implied (but what about that PROC_C1 register + * in the FADT?!?!). Set C2/C3 to max. latency (not supported until + * proven otherwise). + */ + cx_states[0] = 0; + cx_states[1] = 0; + cx_states[2] = MAX_CX_STATE_LATENCY; + cx_states[3] = MAX_CX_STATE_LATENCY; + + /* + * C2 Supported? + * ------------- + * We're only supporting C2 when the latency is <= 100 microseconds, + * and on SMP systems when P_LVL2_UP (which indicates C2 only on UP) + * is not set. + */ + if (acpi_gbl_FACP->plvl2_lat <= 100) { + if (!SMP_system) { + acpi_hw_cx_handlers[2] = acpi_hw_enter_c2; + cx_states[2] = acpi_gbl_FACP->plvl2_lat; + } + + else if (!acpi_gbl_FACP->plvl2_up) { + acpi_hw_cx_handlers[2] = acpi_hw_enter_c2; + cx_states[2] = acpi_gbl_FACP->plvl2_lat; + } + } + + /* + * C3 Supported? + * ------------- + * We're only supporting C3 on UP systems when the latency is + * <= 1000 microseconds 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). + */ + if (acpi_gbl_FACP->plvl3_lat <= 1000) { + if (!SMP_system && (acpi_gbl_FACP->pm2_cnt_blk && + acpi_gbl_FACP->pm2_cnt_len)) + { + acpi_hw_cx_handlers[3] = acpi_hw_enter_c3; + cx_states[3] = acpi_gbl_FACP->plvl3_lat; + } + } + + return(AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_get_cx_handler + * + * PARAMETERS: State - the Cx state + * Handler - pointer to location for the returned handler + * + * RETURN: Status of function + * + * DESCRIPTION: This function is called to get an installed Cx state handler. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_get_cx_handler ( + u32 cx_state, + ACPI_C_STATE_HANDLER *handler) +{ + + if ((cx_state == 0) || (cx_state >= MAX_CX_STATES) || !handler) { + return(AE_BAD_PARAMETER); + } + + *handler = acpi_hw_cx_handlers[cx_state]; + + return(AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_set_cx_handler + * + * PARAMETERS: Cx_state - the Cx state + * Handler - new Cx state handler + * + * RETURN: Status of function + * + * DESCRIPTION: This function is called to install a new Cx state handler. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_hw_set_cx_handler ( + u32 cx_state, + ACPI_C_STATE_HANDLER handler) +{ + + if ((cx_state == 0) || (cx_state >= MAX_CX_STATES) || !handler) { + return(AE_BAD_PARAMETER); + } + + acpi_hw_cx_handlers[cx_state] = handler; + + return(AE_OK); +} + + +/************************************************************************** + * + * FUNCTION: Acpi_hw_local_pow + * + * PARAMETERS: x,y operands + * + * RETURN: result + * + * DESCRIPTION: Compute x ^ y + * + *************************************************************************/ + +NATIVE_UINT +acpi_hw_local_pow ( + NATIVE_UINT x, + NATIVE_UINT y) +{ + NATIVE_UINT i; + NATIVE_UINT result = 1; + + + for (i = 0; i < y; i++) { + result = result * x; + } + + return result; +} + + +/************************************************************************** + * + * FUNCTION: Acpi_hw_enable_throttling + * + * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control) + * register + * + * RETURN: none + * + * DESCRIPTION: Enable throttling by setting the THT_EN bit. + * + *************************************************************************/ + +void +acpi_hw_enable_throttling ( + ACPI_IO_ADDRESS pblk_address) +{ + u32 pblk_value; + + + pblk_value = acpi_os_in32 (pblk_address); + pblk_value = pblk_value | BIT_4; + acpi_os_out32 (pblk_address, pblk_value); + + return; +} + + +/************************************************************************** + * + * FUNCTION: Acpi_hw_disable_throttling + * + * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control) + * register + * + * RETURN: none + * + * DESCRIPTION:Disable throttling by clearing the THT_EN bit + * + *************************************************************************/ + +void +acpi_hw_disable_throttling ( + ACPI_IO_ADDRESS pblk_address) +{ + u32 pblk_value; + + + pblk_value = acpi_os_in32 (pblk_address); + pblk_value = pblk_value & (~(u32)BIT_4); + acpi_os_out32 (pblk_address, pblk_value); + + return; +} + + +/************************************************************************** + * + * FUNCTION: Acpi_hw_get_duty_cycle + * + * PARAMETERS: Duty_offset Pcnt register duty cycle field offset + * Pblk_address Pcnt register address in chipset + * Num_throttle_states # of CPU throttle states this system + * supports + * + * RETURN: none + * + * DESCRIPTION: Get the duty cycle from the chipset + * + *************************************************************************/ + +u32 +acpi_hw_get_duty_cycle ( + u8 duty_offset, + ACPI_IO_ADDRESS pblk_address, + u32 num_throttle_states) +{ + NATIVE_UINT index; + u32 duty32_value; + u32 pcnt_mask_off_duty_field; + + + /* + * Use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)] + * and then shift it into the right position + */ + pcnt_mask_off_duty_field = num_throttle_states - 1; + + /* + * Read in the current value from the port + */ + duty32_value = acpi_os_in32 ((ACPI_IO_ADDRESS) pblk_address); + + /* + * Shift the the value to LSB + */ + for (index = 0; index < (NATIVE_UINT) duty_offset; index++) { + duty32_value = duty32_value >> 1; + } + + /* + * Get the duty field only + */ + duty32_value = duty32_value & pcnt_mask_off_duty_field; + + return ((u32) duty32_value); +} + + +/************************************************************************** + * + * FUNCTION: Acpi_hw_program_duty_cycle + * + * PARAMETERS: Duty_offset Pcnt register duty cycle field offset + * Duty_cycle duty cycle to program into chipset + * Pblk_address Pcnt register address in chipset + * Num_throttle_states # of CPU throttle states this system + * supports + * + * RETURN: none + * + * DESCRIPTION: Program chipset with specified duty cycle by bit-shifting the + * duty cycle bits to the appropriate offset, reading the duty + * cycle register, OR-ing in the duty cycle, and writing it to + * the Pcnt register. + * + *************************************************************************/ + +void +acpi_hw_program_duty_cycle ( + u8 duty_offset, + u32 duty_cycle, + ACPI_IO_ADDRESS pblk_address, + u32 num_throttle_states) +{ + NATIVE_UINT index; + u32 duty32_value; + u32 pcnt_mask_off_duty_field; + u32 port_value; + + + /* + * valid Duty_cycle passed + */ + duty32_value = duty_cycle; + + /* + * use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)] + * and then shift it into the right position + */ + pcnt_mask_off_duty_field = num_throttle_states - 1; + + /* + * Shift the mask + */ + for (index = 0; index < (NATIVE_UINT) duty_offset; index++) { + pcnt_mask_off_duty_field = pcnt_mask_off_duty_field << 1; + duty32_value = duty32_value << 1; + } + + /* + * Read in the current value from the port + */ + port_value = acpi_os_in32 ((ACPI_IO_ADDRESS) pblk_address); + + /* + * Mask off the duty field so we don't OR in junk! + */ + port_value = port_value & (~pcnt_mask_off_duty_field); + + /* + * OR in the bits we want to write out to the port + */ + port_value = (port_value | duty32_value) & (~(u32)BIT_4); + + /* + * write it to the port + */ + acpi_os_out32 ((ACPI_IO_ADDRESS) pblk_address, port_value); + + return; +} + +
\ No newline at end of file diff --git a/drivers/acpi/hardware/hwgpe.c b/drivers/acpi/hardware/hwgpe.c new file mode 100644 index 000000000..0862018c2 --- /dev/null +++ b/drivers/acpi/hardware/hwgpe.c @@ -0,0 +1,208 @@ +/****************************************************************************** + * + * Module Name: hwgpe - Low level GPE enable/disable/clear functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" +#include "events.h" + +#define _COMPONENT HARDWARE + MODULE_NAME ("hwgpe"); + + +u8 decode_to8bit [8] = {1,2,4,8,16,32,64,128}; + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_enable_gpe + * + * PARAMETERS: Gpe_number - The GPE + * + * RETURN: None + * + * DESCRIPTION: Enable a single GPE. + * + ******************************************************************************/ + +void +acpi_hw_enable_gpe ( + u32 gpe_number) +{ + u8 in_byte; + u32 register_index; + u8 bit_mask; + + /* + * Translate GPE number to index into global registers array. + */ + register_index = acpi_gbl_gpe_valid[gpe_number]; + + /* + * Figure out the bit offset for this GPE within the target register. + */ + bit_mask = decode_to8bit [MOD_8 (gpe_number)]; + + /* + * Read the current value of the register, set the appropriate bit + * to enable the GPE, and write out the new register. + */ + in_byte = acpi_os_in8 (acpi_gbl_gpe_registers[register_index].enable_addr); + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, + (u8)(in_byte | bit_mask)); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_disable_gpe + * + * PARAMETERS: Gpe_number - The GPE + * + * RETURN: None + * + * DESCRIPTION: Disable a single GPE. + * + ******************************************************************************/ + +void +acpi_hw_disable_gpe ( + u32 gpe_number) +{ + u8 in_byte; + u32 register_index; + u8 bit_mask; + + /* + * Translate GPE number to index into global registers array. + */ + register_index = acpi_gbl_gpe_valid[gpe_number]; + + /* + * Figure out the bit offset for this GPE within the target register. + */ + bit_mask = decode_to8bit [MOD_8 (gpe_number)]; + + /* + * Read the current value of the register, clear the appropriate bit, + * and write out the new register value to disable the GPE. + */ + in_byte = acpi_os_in8 (acpi_gbl_gpe_registers[register_index].enable_addr); + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].enable_addr, + (u8)(in_byte & ~bit_mask)); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_clear_gpe + * + * PARAMETERS: Gpe_number - The GPE + * + * RETURN: None + * + * DESCRIPTION: Clear a single GPE. + * + ******************************************************************************/ + +void +acpi_hw_clear_gpe ( + u32 gpe_number) +{ + u32 register_index; + u8 bit_mask; + + /* + * Translate GPE number to index into global registers array. + */ + register_index = acpi_gbl_gpe_valid[gpe_number]; + + /* + * Figure out the bit offset for this GPE within the target register. + */ + bit_mask = decode_to8bit [MOD_8 (gpe_number)]; + + /* + * Write a one to the appropriate bit in the status register to + * clear this GPE. + */ + acpi_os_out8 (acpi_gbl_gpe_registers[register_index].status_addr, bit_mask); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_get_gpe_status + * + * PARAMETERS: Gpe_number - The GPE + * + * RETURN: None + * + * DESCRIPTION: Return the status of a single GPE. + * + ******************************************************************************/ + +void +acpi_hw_get_gpe_status ( + u32 gpe_number, + ACPI_EVENT_STATUS *event_status) +{ + u8 in_byte = 0; + u32 register_index = 0; + u8 bit_mask = 0; + + if (!event_status) { + return; + } + + (*event_status) = 0; + + /* + * Translate GPE number to index into global registers array. + */ + register_index = acpi_gbl_gpe_valid[gpe_number]; + + /* + * Figure out the bit offset for this GPE within the target register. + */ + bit_mask = decode_to8bit [MOD_8 (gpe_number)]; + + /* + * Enabled?: + */ + in_byte = acpi_os_in8 (acpi_gbl_gpe_registers[register_index].enable_addr); + + if (bit_mask & in_byte) { + (*event_status) |= ACPI_EVENT_FLAG_ENABLED; + } + + /* + * Set? + */ + in_byte = acpi_os_in8 (acpi_gbl_gpe_registers[register_index].status_addr); + + if (bit_mask & in_byte) { + (*event_status) |= ACPI_EVENT_FLAG_SET; + } +} diff --git a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/hardware/hwregs.c new file mode 100644 index 000000000..947bdd148 --- /dev/null +++ b/drivers/acpi/hardware/hwregs.c @@ -0,0 +1,608 @@ + +/****************************************************************************** + * + * Module Name: hwregs - Read/write access functions for the various ACPI + * control and status registers. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "namesp.h" + +#define _COMPONENT HARDWARE + MODULE_NAME ("hwregs"); + + +/* This matches the #defines in actypes.h. */ + +ACPI_STRING sleep_state_table[] = {"\\_S0_","\\_S1_","\\_S2_","\\_S3_", + "\\_S4_","\\_S4_b","\\_S5_"}; + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_get_bit_shift + * + * PARAMETERS: Mask - Input mask to determine bit shift from. + * Must have at least 1 bit set. + * + * RETURN: Bit location of the lsb of the mask + * + * DESCRIPTION: Returns the bit number for the low order bit that's set. + * + ******************************************************************************/ + +s32 +acpi_hw_get_bit_shift ( + u32 mask) +{ + s32 shift; + + + for (shift = 0; ((mask >> shift) & 1) == 0; shift++) { ; } + + return (shift); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_clear_acpi_status + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Clears all fixed and general purpose status bits + * + ******************************************************************************/ + +void +acpi_hw_clear_acpi_status (void) +{ + u16 gpe_length; + u16 index; + + + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + + acpi_os_out16 (acpi_gbl_FACP->pm1a_evt_blk, (u16) ALL_FIXED_STS_BITS); + + if (acpi_gbl_FACP->pm1b_evt_blk) { + acpi_os_out16 ((u16) acpi_gbl_FACP->pm1b_evt_blk, + (u16) ALL_FIXED_STS_BITS); + } + + /* now clear the GPE Bits */ + + if (acpi_gbl_FACP->gpe0blk_len) { + gpe_length = (u16) DIV_2 (acpi_gbl_FACP->gpe0blk_len); + + for (index = 0; index < gpe_length; index++) { + acpi_os_out8 ((acpi_gbl_FACP->gpe0blk + index), (u8) 0xff); + } + } + + if (acpi_gbl_FACP->gpe1_blk_len) { + gpe_length = (u16) DIV_2 (acpi_gbl_FACP->gpe1_blk_len); + + for (index = 0; index < gpe_length; index++) { + acpi_os_out8 ((acpi_gbl_FACP->gpe1_blk + index), (u8) 0xff); + } + } + + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + return; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_hw_obtain_sleep_type_register_data + * + * PARAMETERS: Sleep_state - Numeric state requested + * *Slp_Typ_a - Pointer to byte to receive SLP_TYPa value + * *Slp_Typ_b - Pointer to byte to receive SLP_TYPb value + * + * RETURN: Status - ACPI status + * + * DESCRIPTION: Acpi_hw_obtain_sleep_type_register_data() obtains the SLP_TYP and + * SLP_TYPb values for the sleep state requested. + * + + ***************************************************************************/ + +ACPI_STATUS +acpi_hw_obtain_sleep_type_register_data ( + u8 sleep_state, + u8 *slp_typ_a, + u8 *slp_typ_b) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *obj_desc; + + + /* + * Validate parameters + */ + + if ((sleep_state > ACPI_S_STATES_MAX) || + !slp_typ_a || !slp_typ_b) + { + return (AE_BAD_PARAMETER); + } + + /* + * Acpi_evaluate the namespace object containing the values for this state + */ + + status = acpi_ns_evaluate_by_name (sleep_state_table[sleep_state], NULL, &obj_desc); + if (AE_OK == status) { + if (obj_desc) { + /* + * We got something, now ensure it is correct. The object must + * be a package and must have at least 2 numeric values as the + * two elements + */ + + if ((obj_desc->common.type != ACPI_TYPE_PACKAGE) || + ((obj_desc->package.elements[0])->common.type != + ACPI_TYPE_NUMBER) || + ((obj_desc->package.elements[1])->common.type != + ACPI_TYPE_NUMBER)) + { + /* Invalid _Sx_ package type or value */ + + REPORT_ERROR ("Object type returned from interpreter differs from expected value"); + status = AE_ERROR; + } + else { + /* + * Valid _Sx_ package size, type, and value + */ + *slp_typ_a = + (u8) (obj_desc->package.elements[0])->number.value; + + *slp_typ_b = + (u8) (obj_desc->package.elements[1])->number.value; + } + + acpi_cm_remove_reference (obj_desc); + } + } + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_hw_register_access + * + * PARAMETERS: Read_write - Either ACPI_READ or ACPI_WRITE. + * Use_lock - Lock the hardware + * Register_id - index of ACPI register to access + * Value - (only used on write) value to write to the + * register. Shifted all the way right. + * + * RETURN: Value written to or read from specified register. This value + * is shifted all the way right. + * + * DESCRIPTION: Generic ACPI register read/write function. + * + ******************************************************************************/ + +u32 +acpi_hw_register_access ( + NATIVE_UINT read_write, + u8 use_lock, + u32 register_id, + ...) /* Value (only used on write) */ +{ + u32 register_value = 0; + u32 mask = 0; + u32 value = 0; + ACPI_IO_ADDRESS gpe_reg = 0; + + + if (read_write == ACPI_WRITE) { + va_list marker; + + va_start (marker, register_id); + value = va_arg (marker, s32); + va_end (marker); + } + + /* + * TBD: [Restructure] May want to split the Acpi_event code and the + * Control code + */ + + /* + * Decode the Register ID + */ + + switch (register_id & REGISTER_BLOCK_MASK) + { + case PM1_EVT: + + if (register_id < TMR_EN) { + /* status register */ + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + } + + + register_value = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk); + if (acpi_gbl_FACP->pm1b_evt_blk) { + register_value |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk); + } + + switch (register_id) + { + case TMR_STS: + mask = TMR_STS_MASK; + break; + + case BM_STS: + mask = BM_STS_MASK; + break; + + case GBL_STS: + mask = GBL_STS_MASK; + break; + + case PWRBTN_STS: + mask = PWRBTN_STS_MASK; + break; + + case SLPBTN_STS: + mask = SLPBTN_STS_MASK; + break; + + case RTC_STS: + mask = RTC_STS_MASK; + break; + + case WAK_STS: + mask = WAK_STS_MASK; + break; + + default: + mask = 0; + break; + } + + if (read_write == ACPI_WRITE) { + /* + * Status registers are different from the rest. Clear by + * writing 1, writing 0 has no effect. So, the only relevent + * information is the single bit we're interested in, all + * others should be written as 0 so they will be left + * unchanged + */ + + value <<= acpi_hw_get_bit_shift (mask); + value &= mask; + + if (value) { + acpi_os_out16 (acpi_gbl_FACP->pm1a_evt_blk, (u16) value); + + if (acpi_gbl_FACP->pm1b_evt_blk) { + acpi_os_out16 (acpi_gbl_FACP->pm1b_evt_blk, (u16) value); + } + + register_value = 0; + } + } + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + } + } + + else { + /* enable register */ + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + } + + register_value = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)); + + if (acpi_gbl_FACP->pm1b_evt_blk) { + register_value |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)); + + } + + switch (register_id) + { + case TMR_EN: + mask = TMR_EN_MASK; + break; + + case GBL_EN: + mask = GBL_EN_MASK; + break; + + case PWRBTN_EN: + mask = PWRBTN_EN_MASK; + break; + + case SLPBTN_EN: + mask = SLPBTN_EN_MASK; + break; + + case RTC_EN: + mask = RTC_EN_MASK; + break; + + default: + mask = 0; + break; + } + + if (read_write == ACPI_WRITE) { + register_value &= ~mask; + value <<= acpi_hw_get_bit_shift (mask); + value &= mask; + register_value |= value; + + acpi_os_out16 ((acpi_gbl_FACP->pm1a_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)), + (u16) register_value); + + if (acpi_gbl_FACP->pm1b_evt_blk) { + acpi_os_out16 ((acpi_gbl_FACP->pm1b_evt_blk + + DIV_2 (acpi_gbl_FACP->pm1_evt_len)), + (u16) register_value); + } + } + if(ACPI_MTX_LOCK == use_lock) { + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + } + } + break; + + + case PM1_CONTROL: + + register_value = 0; + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + } + + if (register_id != SLP_TYPE_B) { + /* + * SLP_TYPx registers are written differently + * than any other control registers with + * respect to A and B registers. The value + * for A may be different than the value for B + */ + + register_value = (u32) acpi_os_in16 (acpi_gbl_FACP->pm1a_cnt_blk); + } + + if (acpi_gbl_FACP->pm1b_cnt_blk && register_id != (s32) SLP_TYPE_A) { + register_value |= (u32) acpi_os_in16 (acpi_gbl_FACP->pm1b_cnt_blk); + } + + switch (register_id) + { + case SCI_EN: + mask = SCI_EN_MASK; + break; + + case BM_RLD: + mask = BM_RLD_MASK; + break; + + case GBL_RLS: + mask = GBL_RLS_MASK; + break; + + case SLP_TYPE_A: + case SLP_TYPE_B: + mask = SLP_TYPE_X_MASK; + break; + + case SLP_EN: + mask = SLP_EN_MASK; + break; + + default: + mask = 0; + break; + } + + if (read_write == ACPI_WRITE) { + register_value &= ~mask; + value <<= acpi_hw_get_bit_shift (mask); + value &= mask; + register_value |= value; + + /* + * SLP_TYPE_x registers are written differently + * than any other control registers with + * respect to A and B registers. The value + * for A may be different than the value for B + */ + + if (register_id != SLP_TYPE_B) { + if (mask == SLP_EN_MASK) { + disable(); /* disable interrupts */ + } + + acpi_os_out16 (acpi_gbl_FACP->pm1a_cnt_blk, (u16) register_value); + + if (mask == SLP_EN_MASK) { + /* + * Enable interrupts, the SCI handler is likely going to + * be invoked as soon as interrupts are enabled, since gpe's + * and most fixed resume events also generate SCI's. + */ + enable(); + } + } + + if (acpi_gbl_FACP->pm1b_cnt_blk && register_id != (s32) SLP_TYPE_A) { + acpi_os_out16 (acpi_gbl_FACP->pm1b_cnt_blk, (u16) register_value); + } + } + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + } + break; + + + case PM2_CONTROL: + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + } + + register_value = (u32) acpi_os_in16 (acpi_gbl_FACP->pm2_cnt_blk); + switch (register_id) + { + case ARB_DIS: + mask = ARB_DIS_MASK; + break; + + default: + mask = 0; + break; + } + + if (read_write == ACPI_WRITE) { + register_value &= ~mask; + value <<= acpi_hw_get_bit_shift (mask); + value &= mask; + register_value |= value; + + acpi_os_out16 (acpi_gbl_FACP->pm2_cnt_blk, (u16) register_value); + } + + if (ACPI_MTX_LOCK == use_lock) { + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + } + break; + + + case PM_TIMER: + + register_value = acpi_os_in32 (acpi_gbl_FACP->pm_tmr_blk); + mask = 0xFFFFFFFF; + break; + + + case GPE1_EN_BLOCK: + + gpe_reg = (acpi_gbl_FACP->gpe1_blk + acpi_gbl_FACP->gpe1_base) + + (gpe_reg + (DIV_2 (acpi_gbl_FACP->gpe1_blk_len))); + + + case GPE1_STS_BLOCK: + + if (!gpe_reg) { + gpe_reg = (acpi_gbl_FACP->gpe1_blk + acpi_gbl_FACP->gpe1_base); + } + + + case GPE0_EN_BLOCK: + + if (!gpe_reg) { + gpe_reg = acpi_gbl_FACP->gpe0blk + DIV_2 (acpi_gbl_FACP->gpe0blk_len); + } + + + case GPE0_STS_BLOCK: + + if (!gpe_reg) { + gpe_reg = acpi_gbl_FACP->gpe0blk; + } + + /* Determine the bit to be accessed */ + + mask = (((u32) register_id) & BIT_IN_REGISTER_MASK); + mask = 1 << (mask-1); + + /* The base address of the GPE 0 Register Block */ + /* Plus 1/2 the length of the GPE 0 Register Block */ + /* The enable register is the register following the Status Register */ + /* and each register is defined as 1/2 of the total Register Block */ + + /* This sets the bit within Enable_bit that needs to be written to */ + /* the register indicated in Mask to a 1, all others are 0 */ + + if (mask > LOW_BYTE) { + /* Shift the value 1 byte to the right and add 1 to the register */ + + mask >>= ONE_BYTE; + gpe_reg++; + } + + /* Now get the current Enable Bits in the selected Reg */ + + if(ACPI_MTX_LOCK == use_lock) { + acpi_cm_acquire_mutex (ACPI_MTX_HARDWARE); + } + + register_value = (u32) acpi_os_in8 (gpe_reg); + if (read_write == ACPI_WRITE) { + register_value &= ~mask; + value <<= acpi_hw_get_bit_shift (mask); + value &= mask; + register_value |= value; + + /* This write will put the Action state into the General Purpose */ + + /* Enable Register indexed by the value in Mask */ + + acpi_os_out8 (gpe_reg, (u8) register_value); + register_value = (u32) acpi_os_in8 (gpe_reg); + } + + if(ACPI_MTX_LOCK == use_lock) { + acpi_cm_release_mutex (ACPI_MTX_HARDWARE); + } + break; + + + case PROCESSOR_BLOCK: + default: + + mask = 0; + break; + } + + + register_value &= mask; + register_value >>= acpi_hw_get_bit_shift (mask); + + return (register_value); +} diff --git a/drivers/acpi/hardware/hwxface.c b/drivers/acpi/hardware/hwxface.c new file mode 100644 index 000000000..f92e59c5f --- /dev/null +++ b/drivers/acpi/hardware/hwxface.c @@ -0,0 +1,576 @@ + +/****************************************************************************** + * + * Name: hwxface.c - Hardware access external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "acpi.h" +#include "namesp.h" +#include "hardware.h" + +#define _COMPONENT HARDWARE + MODULE_NAME ("hwxface"); + + +/****************************************************************************** + * + * Hardware globals + * + ******************************************************************************/ + + +ACPI_C_STATE_HANDLER acpi_hw_cx_handlers[MAX_CX_STATES] = + {NULL, acpi_hw_enter_c1, NULL, NULL}; + +u32 acpi_hw_active_cx_state = 1; + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_processor_throttling_info + * + * PARAMETERS: Processor_handle - handle for the cpu to get info about + * User_buffer - caller supplied buffer + * + * RETURN: Status of function + * + * DESCRIPTION: Get throttling capabilities for the processor, this routine + * builds the data directly into the callers buffer + * + ****************************************************************************/ + +ACPI_STATUS +acpi_get_processor_throttling_info ( + ACPI_HANDLE processor_handle, + ACPI_BUFFER *user_buffer) +{ + NATIVE_UINT percent_step; + NATIVE_UINT next_percent; + NATIVE_UINT num_throttle_states; + NATIVE_UINT buffer_space_needed; + NATIVE_UINT i; + u8 duty_offset; + u8 duty_width; + ACPI_NAMED_OBJECT *cpu_entry; + ACPI_OBJECT_INTERNAL *cpu_obj; + ACPI_CPU_THROTTLING_STATE *state_ptr; + + + /* + * Have to at least have a buffer to return info in + */ + if (!user_buffer) { + return(AE_BAD_PARAMETER); + } + + /* + * Convert and validate the device handle + */ + + cpu_entry = acpi_ns_convert_handle_to_entry (processor_handle); + if (!cpu_entry) { + return (AE_BAD_PARAMETER); + } + + /* + * Check for an existing internal object + */ + + cpu_obj = acpi_ns_get_attached_object ((ACPI_HANDLE) cpu_entry); + if (!cpu_obj) { + return (AE_NOT_FOUND); + } + + duty_offset = acpi_gbl_FACP->duty_offset; + duty_width = acpi_gbl_FACP->duty_width; + + /* + * P0 must always have a P_BLK all others may be null + * in either case, we can't thottle a processor that has no P_BLK + * + * Also if no Duty width, one state and it is 100% + * + */ + if (!cpu_obj->processor.pblk_length || !duty_width || + (0xFFFF < cpu_obj->processor.pblk_address)) + { + /* + * Acpi_even though we can't throttle, we still have one state (100%) + */ + num_throttle_states = 1; + } + + else { + num_throttle_states = (int) acpi_hw_local_pow (2,duty_width); + } + + buffer_space_needed = num_throttle_states * sizeof (ACPI_CPU_THROTTLING_STATE); + + if ((user_buffer->length < buffer_space_needed) || !user_buffer->pointer) { + user_buffer->length = buffer_space_needed; + return (AE_BUFFER_OVERFLOW); + } + + user_buffer->length = buffer_space_needed; + state_ptr = (ACPI_CPU_THROTTLING_STATE *) user_buffer->pointer; + percent_step = 1000 / num_throttle_states; + + /* + * Build each entry in the buffer. Note that we're using the value + * 1000 and dividing each state by 10 to better avoid round-off + * accumulation. Also note that the throttling STATES are ordered + * sequentially from 100% (state 0) on down (e.g. 87.5% = state 1), + * which is exactly opposite from duty cycle values (12.5% = state 1). + */ + for (i = 0, next_percent = 1000; i < num_throttle_states; i++) { + state_ptr[i].state_number = i; + state_ptr[i].percent_of_clock = next_percent / 10; + next_percent -= percent_step; + } + + return(AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_processor_throttling_state + * + * PARAMETERS: Processor_handle - handle for the cpu to throttle + * Throttle_state - throttling state to enter + * + * RETURN: Status of function + * + * DESCRIPTION: Get current hardware throttling state + * + ****************************************************************************/ + +ACPI_STATUS +acpi_get_processor_throttling_state ( + ACPI_HANDLE processor_handle, + u32 *throttle_state) +{ + ACPI_NAMED_OBJECT *cpu_entry; + ACPI_OBJECT_INTERNAL *cpu_obj; + u32 num_throttle_states; + u32 duty_cycle; + u8 duty_offset; + u8 duty_width; + + + /* Convert and validate the device handle */ + + cpu_entry = acpi_ns_convert_handle_to_entry (processor_handle); + if (!cpu_entry || !throttle_state) { + return (AE_BAD_PARAMETER); + } + + /* Check for an existing internal object */ + + cpu_obj = acpi_ns_get_attached_object ((ACPI_HANDLE) cpu_entry); + if (!cpu_obj) { + return (AE_NOT_FOUND); + } + + duty_offset = acpi_gbl_FACP->duty_offset; + duty_width = acpi_gbl_FACP->duty_width; + + /* + * Must have a valid P_BLK P0 must have a P_BLK all others may be null + * in either case, we can't thottle a processor that has no P_BLK + * that means we are in the only supported state (0 - 100%) + * + * also, if Duty_width is zero there are no additional states + */ + if (!cpu_obj->processor.pblk_length || !duty_width || + (0xFFFF < cpu_obj->processor.pblk_address)) + { + *throttle_state = 0; + return(AE_OK); + } + + num_throttle_states = (u32) acpi_hw_local_pow (2,duty_width); + + /* + * Get the current duty cycle value. + */ + duty_cycle = acpi_hw_get_duty_cycle (duty_offset, + cpu_obj->processor.pblk_address, + num_throttle_states); + + /* + * Convert duty cycle to throttling state (invert). + */ + if (duty_cycle == 0) { + *throttle_state = 0; + } + + else { + *throttle_state = num_throttle_states - duty_cycle; + } + + return(AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_set_processor_throttling_state + * + * PARAMETERS: Processor_handle - handle for the cpu to throttle + * Throttle_state - throttling state to enter + * + * RETURN: Status of function + * + * DESCRIPTION: Set hardware into requested throttling state, the handle + * passed in must have a valid P_BLK + * + ****************************************************************************/ + +ACPI_STATUS +acpi_set_processor_throttling_state ( + ACPI_HANDLE processor_handle, + u32 throttle_state) +{ + ACPI_NAMED_OBJECT *cpu_entry; + ACPI_OBJECT_INTERNAL *cpu_obj; + u32 num_throttle_states = 0; + u8 duty_offset = 0; + u8 duty_width = 0; + u32 duty_cycle = 0; + + + /* Convert and validate the device handle */ + + cpu_entry = acpi_ns_convert_handle_to_entry (processor_handle); + if (!cpu_entry) { + return (AE_BAD_PARAMETER); + } + + /* Check for an existing internal object */ + + cpu_obj = acpi_ns_get_attached_object ((ACPI_HANDLE) cpu_entry); + if (!cpu_obj) { + return (AE_NOT_FOUND); + } + + duty_offset = acpi_gbl_FACP->duty_offset; + duty_width = acpi_gbl_FACP->duty_width; + + /* + * Must have a valid P_BLK P0 must have a P_BLK all others may be null + * in either case, we can't thottle a processor that has no P_BLK + * that means we are in the only supported state (0 - 100%) + * + * also, if Duty_width is zero there are no additional states + */ + if (!cpu_obj->processor.pblk_length || !duty_width || + (0xFFFF < cpu_obj->processor.pblk_address)) + { + /* + * If caller wants to set the state to the only state we handle + * we're done. + */ + if (throttle_state == 0) { + return (AE_OK); + } + + /* + * Can't set this state + */ + return (AE_SUPPORT); + } + + num_throttle_states = (int) acpi_hw_local_pow (2,duty_width); + + /* + * Convert throttling state to duty cycle (invert). + */ + if (throttle_state > 0) { + duty_cycle = num_throttle_states - throttle_state; + } + + /* + * Turn off throttling (don't muck with the h/w while throttling). + */ + acpi_hw_disable_throttling (cpu_obj->processor.pblk_address); + + /* + * Program the throttling state. + */ + acpi_hw_program_duty_cycle (duty_offset, duty_cycle, + cpu_obj->processor.pblk_address, num_throttle_states); + + /* + * Only enable throttling for non-zero states (0 - 100%) + */ + if (throttle_state) { + acpi_hw_enable_throttling (cpu_obj->processor.pblk_address); + } + + return(AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_processor_cx_info + * + * PARAMETERS: Processor_handle - handle for the cpu return info about + * User_buffer - caller supplied buffer + * + * RETURN: Status of function + * + * DESCRIPTION: Get Cx state latencies, this routine + * builds the data directly into the callers buffer + * + * + ****************************************************************************/ + +ACPI_STATUS +acpi_get_processor_cx_info ( + ACPI_HANDLE processor_handle, + ACPI_BUFFER *user_buffer) +{ + ACPI_STATUS status = AE_OK; + u32 cx_state_latencies[4] = {0, 0, 0, 0}; + NATIVE_UINT buffer_space_needed = 0; + ACPI_CX_STATE *state_ptr = NULL; + NATIVE_UINT i = 0; + + + /* + * Have to at least have a buffer to return info in + */ + if (!user_buffer) { + return (AE_BAD_PARAMETER); + } + + status = acpi_hw_get_cx_info (cx_state_latencies); + if (ACPI_FAILURE (status)) { + return (status); + } + + buffer_space_needed = 4 * sizeof (ACPI_CX_STATE); + + if ((user_buffer->length < buffer_space_needed) || !user_buffer->pointer) { + user_buffer->length = buffer_space_needed; + return (AE_BUFFER_OVERFLOW); + } + + user_buffer->length = buffer_space_needed; + + state_ptr = (ACPI_CX_STATE *) user_buffer->pointer; + + for (i = 0; i < 4; i++) { + state_ptr[i].state_number = i; + state_ptr[i].latency = cx_state_latencies[i]; + } + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_set_processor_sleep_state + * + * PARAMETERS: Processor_handle - handle for the cpu return info about + * Cx_state - the Cx sleeping state (C1-C3) to make + * 'active' + * + * RETURN: Status of function + * + * DESCRIPTION: Sets which Cx state will be used during calls to + * Acpi_processor_sleep () + * + ****************************************************************************/ + +ACPI_STATUS +acpi_set_processor_sleep_state ( + ACPI_HANDLE processor_handle, + u32 cx_state) +{ + ACPI_STATUS status; + + + status = acpi_hw_set_cx (cx_state); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_processor_sleep + * + * PARAMETERS: Processor_handle - handle for the cpu to put to sleep (Cx) + * Time_sleeping - time (in microseconds) elapsed while + * sleeping + * + * RETURN: Status of function + * + * DESCRIPTION: Puts the processor into the currently active sleep state (Cx) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_processor_sleep ( + ACPI_HANDLE processor_handle, + u32 *pm_timer_ticks) +{ + ACPI_NAMED_OBJECT *cpu_entry = NULL; + ACPI_OBJECT_INTERNAL *cpu_obj = NULL; + ACPI_IO_ADDRESS pblk_address = 0; + + + /* + * Convert Processor_handle to Pblk_addres... + */ + + /* Convert and validate the device handle */ + + cpu_entry = acpi_ns_convert_handle_to_entry (processor_handle); + if (!cpu_entry) { + return AE_BAD_PARAMETER; + } + + /* Check for an existing internal object */ + + cpu_obj = acpi_ns_get_attached_object ((ACPI_HANDLE) cpu_entry); + if (!cpu_obj) { + return AE_NOT_FOUND; + } + + /* Get the processor register block (P_BLK) address */ + + pblk_address = cpu_obj->processor.pblk_address; + if (!cpu_obj->processor.pblk_length) { + /* Ensure a NULL addresss (note that P_BLK isn't required for C1) */ + + pblk_address = 0; + } + + /* + * Enter the currently active Cx sleep state. + */ + return acpi_hw_enter_cx (pblk_address, pm_timer_ticks); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_timer + * + * PARAMETERS: none + * + * RETURN: Current value of the ACPI PMT (timer) + * + * DESCRIPTION: Obtains current value of ACPI PMT + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_timer ( + u32 *out_ticks) +{ + + if (!out_ticks) { + return (AE_BAD_PARAMETER); + } + + *out_ticks = acpi_hw_pmt_ticks (); + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_set_firmware_waking_vector + * + * PARAMETERS: Physical_address - Physical address of ACPI real mode + * entry point. + * + * RETURN: AE_OK or AE_ERROR + * + * DESCRIPTION: Access function for d_firmware_waking_vector field in FACS + * + ******************************************************************************/ + +ACPI_STATUS +acpi_set_firmware_waking_vector ( + void *physical_address) +{ + + /* Make sure that we have an FACS */ + + if (!acpi_gbl_FACS) { + return (AE_NO_ACPI_TABLES); + } + + /* Set the vector */ + + * ((void **) acpi_gbl_FACS->firmware_waking_vector) = physical_address; + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_firmware_waking_vector + * + * PARAMETERS: *Physical_address - Output buffer where contents of + * the d_firmware_waking_vector field of + * the FACS will be stored. + * + * RETURN: Status + * + * DESCRIPTION: Access function for d_firmware_waking_vector field in FACS + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_firmware_waking_vector ( + void **physical_address) +{ + + if (!physical_address) { + return (AE_BAD_PARAMETER); + } + + /* Make sure that we have an FACS */ + + if (!acpi_gbl_FACS) { + return (AE_NO_ACPI_TABLES); + } + + /* Get the vector */ + + *physical_address = * ((void **) acpi_gbl_FACS->firmware_waking_vector); + + + return (AE_OK); +} + + diff --git a/drivers/acpi/include/acenv.h b/drivers/acpi/include/acenv.h new file mode 100644 index 000000000..f2738537f --- /dev/null +++ b/drivers/acpi/include/acenv.h @@ -0,0 +1,302 @@ + +/****************************************************************************** + * + * Name: acenv.h - Generation environment specific items + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACENV_H__ +#define __ACENV_H__ + + +/* + * Environment configuration. The purpose of this file is to interface to the + * local generation environment. + * + * 1) ACPI_USE_SYSTEM_CLIBRARY - Define this if linking to an actual C library. + * Otherwise, local versions of string/memory functions will be used. + * 2) ACPI_USE_STANDARD_HEADERS - Define this if linking to a C library and + * the standard header files may be used. + * + * The ACPI subsystem only uses low level C library functions that do not call + * operating system services and may therefore be inlined in the code. + * + * It may be necessary to tailor these include files to the target + * generation environment. + * + * + * Functions and constants used from each header: + * + * string.h: memcpy + * memset + * strcat + * strcmp + * strcpy + * strlen + * strncmp + * strncat + * strncpy + * + * stdlib.h: strtoul + * + * stdarg.h: va_list + * va_arg + * va_start + * va_end + * + */ + + +/* + * Environment-specific configuration + */ + +#ifdef _LINUX + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <asm/system.h> + +/* Single threaded */ + +#define ACPI_APPLICATION + +/* Use native Linux string library */ + +#define ACPI_USE_SYSTEM_CLIBRARY + +/* Special functions */ + +#define strtoul simple_strtoul + +#else + +/* All other environments */ + +#define ACPI_USE_STANDARD_HEADERS + +#endif + + +/****************************************************************************** + * + * C library configuration + * + *****************************************************************************/ + +#ifdef ACPI_USE_SYSTEM_CLIBRARY +/* + * Use the standard C library headers. + * We want to keep these to a minimum. + * + */ + +#ifdef ACPI_USE_STANDARD_HEADERS +/* + * Use the standard headers from the standard locations + */ +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#endif /* ACPI_USE_STANDARD_HEADERS */ + +/* + * We will be linking to the standard Clib functions + */ + +#define STRSTR(s1,s2) strstr((char *) (s1), (char *) (s2)) +#define STRUPR(s) strupr((char *) (s)) +#define STRLEN(s) strlen((char *) (s)) +#define STRCPY(d,s) strcpy((char *) (d), (char *) (s)) +#define STRNCPY(d,s,n) strncpy((char *) (d), (char *) (s), (n)) +#define STRNCMP(d,s,n) strncmp((char *) (d), (char *) (s), (n)) +#define STRCMP(d,s) strcmp((char *) (d), (char *) (s)) +#define STRCAT(d,s) strcat((char *) (d), (char *) (s)) +#define STRNCAT(d,s,n) strncat((char *) (d), (char *) (s), (n)) +#define STRTOUL(d,s,n) strtoul((char *) (d), (char **) (s), (n)) +#define MEMCPY(d,s,n) memcpy(d, s, (size_t) n) +#define MEMSET(d,s,n) memset(d, s, (size_t) n) +#define TOUPPER toupper +#define TOLOWER tolower + + +/****************************************************************************** + * + * Not using native C library, use local implementations + * + *****************************************************************************/ +#else + +/* + * Use local definitions of C library macros and functions + * NOTE: The function implementations may not be as efficient + * as an inline or assembly code implementation provided by a + * native C library. + */ + +#ifndef va_arg + +#ifndef _VALIST +#define _VALIST +typedef char *va_list; +#endif /* _VALIST */ + +/* + * Storage alignment properties + */ + +#define _AUPBND (sizeof(int) - 1) +#define _ADNBND (sizeof(int) - 1) + +/* + * Variable argument list macro definitions + */ + +#define _bnd(X, bnd) (((sizeof(X)) + (bnd)) & (~(bnd))) +#define va_arg(ap, T) (*(T *)(((ap) += ((_bnd(T, _AUPBND))) \ + - (_bnd(T, _ADNBND))))) +#define va_end(ap) (void)0 +#define va_start(ap, A) (void) ((ap) = (((char *)&(A)) \ + + (_bnd(A, _AUPBND)))) + +#endif /* va_arg */ + + +#define STRSTR(s1,s2) acpi_cm_strstr ((char *) (s1), (char *) (s2)) +#define STRUPR(s) acpi_cm_strupr ((char *) (s)) +#define STRLEN(s) acpi_cm_strlen ((char *) (s)) +#define STRCPY(d,s) acpi_cm_strcpy ((char *) (d), (char *) (s)) +#define STRNCPY(d,s,n) acpi_cm_strncpy ((char *) (d), (char *) (s), (n)) +#define STRNCMP(d,s,n) acpi_cm_strncmp ((char *) (d), (char *) (s), (n)) +#define STRCMP(d,s) acpi_cm_strcmp ((char *) (d), (char *) (s)) +#define STRCAT(d,s) acpi_cm_strcat ((char *) (d), (char *) (s)) +#define STRNCAT(d,s,n) acpi_cm_strncat ((char *) (d), (char *) (s), (n)) +#define STRTOUL(d,s,n) acpi_cm_strtoul ((char *) (d), (char **) (s), (n)) +#define MEMCPY(d,s,n) acpi_cm_memcpy ((void *) (d), (const void *) (s), (n)) +#define MEMSET(d,v,n) acpi_cm_memset ((void *) (d), (v), (n)) +#define TOUPPER acpi_cm_to_upper +#define TOLOWER acpi_cm_to_lower + +#endif /* ACPI_USE_SYSTEM_CLIBRARY */ + + +/****************************************************************************** + * + * Assembly code macros + * + *****************************************************************************/ + +/* + * Handle platform- and compiler-specific assembly language differences. + * + * Notes: + * 1) Interrupt 3 is used to break into a debugger + * 2) Interrupts are turned off during ACPI register setup + */ + + +#ifdef __GNUC__ + +#ifdef __ia64__ +#define _IA64 +#endif + +#define ACPI_ASM_MACROS +#define causeinterrupt(level) +#define BREAKPOINT3 +#define disable() __cli() +#define enable() __sti() +#define halt() __asm__ __volatile__ ("sti; hlt":::"memory") +#define wbinvd() + + +/*! [Begin] no source code translation + * + * A brief explanation as GNU inline assembly is a bit hairy + * %0 is the output parameter in EAX ("=a") + * %1 and %2 are the input parameters in ECX ("c") and an immediate value ("i") respectively + * All actual register references are preceded with "%%" as in "%%edx" + * Immediate values in the assembly are preceded by "$" as in "$0x1" + * The final asm parameter is the non-output registers altered by the operation + */ +#define ACPI_ACQUIRE_GLOBAL_LOCK(GLptr, Acq) \ + do { \ + int dummy; \ + asm("1: movl (%1),%%eax;" \ + "movl %%eax,%%edx;" \ + "andl %2,%%edx;" \ + "btsl $0x1,%%edx;" \ + "adcl $0x0,%%edx;" \ + "lock; cmpxchgl %%edx,(%1);" \ + "jnz 1b;" \ + "cmpb $0x3,%%dl;" \ + "sbbl %%eax,%%eax" \ + :"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~1L):"dx"); \ + } while(0) + +#define ACPI_RELEASE_GLOBAL_LOCK(GLptr, Acq) \ + do { \ + int dummy; \ + asm("1: movl (%1),%%eax;" \ + "movl %%eax,%%edx;" \ + "andl %2,%%edx;" \ + "lock; cmpxchgl %%edx,(%1);" \ + "jnz 1b;" \ + "andl $0x1,%%eax" \ + :"=a"(Acq),"=c"(dummy):"c"(GLptr),"i"(~3L):"dx"); \ + } while(0) +/*! [End] no source code translation !*/ + +#endif /* __GNUC__ */ + + +#ifndef ACPI_ASM_MACROS + +/* Unrecognized compiler, use defaults */ + +#define ACPI_ASM_MACROS +#define causeinterrupt(level) +#define BREAKPOINT3 +#define disable() +#define enable() +#define halt() + +#define ACPI_ACQUIRE_GLOBAL_LOCK(Glptr, acq) +#define ACPI_RELEASE_GLOBAL_LOCK(Glptr, acq) + +#endif /* ACPI_ASM_MACROS */ + + +#ifdef ACPI_APPLICATION + +/* Don't want software interrupts within a ring3 application */ + +#undef causeinterrupt +#undef BREAKPOINT3 +#define causeinterrupt(level) +#define BREAKPOINT3 +#endif + + +#endif /* __ACENV_H__ */ diff --git a/drivers/acpi/include/acexcep.h b/drivers/acpi/include/acexcep.h new file mode 100644 index 000000000..4913ffd2c --- /dev/null +++ b/drivers/acpi/include/acexcep.h @@ -0,0 +1,162 @@ + +/****************************************************************************** + * + * Name: acexcep.h - Exception codes returned by the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACEXCEP_H__ +#define __ACEXCEP_H__ + + +/* + * Exceptions returned by external ACPI interfaces + */ + +#define ACPI_SUCCESS(a) (!(a)) +#define ACPI_FAILURE(a) (a) + +#define AE_OK (ACPI_STATUS) 0x0000 +#define AE_CTRL_RETURN_VALUE (ACPI_STATUS) 0x0001 +#define AE_CTRL_PENDING (ACPI_STATUS) 0x0002 +#define AE_CTRL_TERMINATE (ACPI_STATUS) 0x0003 +#define AE_CTRL_TRUE (ACPI_STATUS) 0x0004 +#define AE_CTRL_FALSE (ACPI_STATUS) 0x0005 +#define AE_CTRL_DEPTH (ACPI_STATUS) 0x0006 +#define AE_CTRL_RESERVED (ACPI_STATUS) 0x0007 +#define AE_AML_ERROR (ACPI_STATUS) 0x0008 +#define AE_AML_PARSE (ACPI_STATUS) 0x0009 +#define AE_AML_BAD_OPCODE (ACPI_STATUS) 0x000A +#define AE_AML_NO_OPERAND (ACPI_STATUS) 0x000B +#define AE_AML_OPERAND_TYPE (ACPI_STATUS) 0x000C +#define AE_AML_OPERAND_VALUE (ACPI_STATUS) 0x000D +#define AE_AML_UNINITIALIZED_LOCAL (ACPI_STATUS) 0x000E +#define AE_AML_UNINITIALIZED_ARG (ACPI_STATUS) 0x000F +#define AE_AML_UNINITIALIZED_ELEMENT (ACPI_STATUS) 0x0010 +#define AE_AML_NUMERIC_OVERFLOW (ACPI_STATUS) 0x0011 +#define AE_AML_REGION_LIMIT (ACPI_STATUS) 0x0012 +#define AE_AML_BUFFER_LIMIT (ACPI_STATUS) 0x0013 +#define AE_AML_PACKAGE_LIMIT (ACPI_STATUS) 0x0014 +#define AE_AML_DIVIDE_BY_ZERO (ACPI_STATUS) 0x0015 +#define AE_AML_BAD_NAME (ACPI_STATUS) 0x0016 +#define AE_AML_NAME_NOT_FOUND (ACPI_STATUS) 0x0017 +#define AE_AML_INTERNAL (ACPI_STATUS) 0x0018 +#define AE_AML_RESERVED (ACPI_STATUS) 0x0019 +#define AE_ERROR (ACPI_STATUS) 0x001A +#define AE_NO_ACPI_TABLES (ACPI_STATUS) 0x001B +#define AE_NO_NAMESPACE (ACPI_STATUS) 0x001C +#define AE_NO_MEMORY (ACPI_STATUS) 0x001D +#define AE_BAD_SIGNATURE (ACPI_STATUS) 0x001E +#define AE_BAD_HEADER (ACPI_STATUS) 0x001F +#define AE_BAD_CHECKSUM (ACPI_STATUS) 0x0020 +#define AE_BAD_PARAMETER (ACPI_STATUS) 0x0021 +#define AE_BAD_CHARACTER (ACPI_STATUS) 0x0022 +#define AE_BAD_PATHNAME (ACPI_STATUS) 0x0023 +#define AE_BAD_DATA (ACPI_STATUS) 0x0024 +#define AE_BAD_ADDRESS (ACPI_STATUS) 0x0025 +#define AE_NOT_FOUND (ACPI_STATUS) 0x0026 +#define AE_NOT_EXIST (ACPI_STATUS) 0x0027 +#define AE_EXIST (ACPI_STATUS) 0x0028 +#define AE_TYPE (ACPI_STATUS) 0x0029 +#define AE_NULL_OBJECT (ACPI_STATUS) 0x002A +#define AE_NULL_ENTRY (ACPI_STATUS) 0x002B +#define AE_BUFFER_OVERFLOW (ACPI_STATUS) 0x002C +#define AE_STACK_OVERFLOW (ACPI_STATUS) 0x002D +#define AE_STACK_UNDERFLOW (ACPI_STATUS) 0x002E +#define AE_NOT_IMPLEMENTED (ACPI_STATUS) 0x002F +#define AE_VERSION_MISMATCH (ACPI_STATUS) 0x0030 +#define AE_SUPPORT (ACPI_STATUS) 0x0031 +#define AE_SHARE (ACPI_STATUS) 0x0032 +#define AE_LIMIT (ACPI_STATUS) 0x0033 +#define AE_TIME (ACPI_STATUS) 0x0034 +#define AE_UNKNOWN_STATUS (ACPI_STATUS) 0x0035 +#define ACPI_MAX_STATUS (ACPI_STATUS) 0x0035 +#define ACPI_NUM_STATUS (ACPI_STATUS) 0x0036 + + +#ifdef DEFINE_ACPI_GLOBALS + +/* + * String versions of the exception codes above + * These strings must match the corresponding defines exactly + */ +static char *acpi_gbl_exception_names[] = +{ + "AE_OK", + "AE_CTRL_RETURN_VALUE", + "AE_CTRL_PENDING", + "AE_CTRL_TERMINATE", + "AE_CTRL_TRUE", + "AE_CTRL_FALSE", + "AE_CTRL_DEPTH", + "AE_CTRL_RESERVED", + "AE_AML_ERROR", + "AE_AML_PARSE", + "AE_AML_BAD_OPCODE", + "AE_AML_NO_OPERAND", + "AE_AML_OPERAND_TYPE", + "AE_AML_OPERAND_VALUE", + "AE_AML_UNINITIALIZED_LOCAL", + "AE_AML_UNINITIALIZED_ARG", + "AE_AML_UNINITIALIZED_ELEMENT", + "AE_AML_NUMERIC_OVERFLOW", + "AE_AML_REGION_LIMIT", + "AE_AML_BUFFER_LIMIT", + "AE_AML_PACKAGE_LIMIT", + "AE_AML_DIVIDE_BY_ZERO", + "AE_AML_BAD_NAME", + "AE_AML_NAME_NOT_FOUND", + "AE_AML_INTERNAL", + "AE_AML_RESERVED", + "AE_ERROR", + "AE_NO_ACPI_TABLES", + "AE_NO_NAMESPACE", + "AE_NO_MEMORY", + "AE_BAD_SIGNATURE", + "AE_BAD_HEADER", + "AE_BAD_CHECKSUM", + "AE_BAD_PARAMETER", + "AE_BAD_CHARACTER", + "AE_BAD_PATHNAME", + "AE_BAD_DATA", + "AE_BAD_ADDRESS", + "AE_NOT_FOUND", + "AE_NOT_EXIST", + "AE_EXIST", + "AE_TYPE", + "AE_NULL_OBJECT", + "AE_NULL_ENTRY", + "AE_BUFFER_OVERFLOW", + "AE_STACK_OVERFLOW", + "AE_STACK_UNDERFLOW", + "AE_NOT_IMPLEMENTED", + "AE_VERSION_MISMATCH", + "AE_SUPPORT", + "AE_SHARE", + "AE_LIMIT", + "AE_TIME", + "AE_UNKNOWN_STATUS" +}; + +#endif /* DEFINE_ACPI_GLOBALS */ + + +#endif /* __ACEXCEP_H__ */ diff --git a/drivers/acpi/include/acobject.h b/drivers/acpi/include/acobject.h new file mode 100644 index 000000000..449e23bb4 --- /dev/null +++ b/drivers/acpi/include/acobject.h @@ -0,0 +1,508 @@ + +/****************************************************************************** + * + * Name: acobject.h - Definition of ACPI_OBJECT_INTERNAL (Internal object only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ACOBJECT_H +#define _ACOBJECT_H + +#include "actypes.h" +#include "macros.h" +#include "internal.h" + +/* + * The ACPI_OBJECT_INTERNAL is used to pass AML operands from the dispatcher + * to the interpreter, and to keep track of the various handlers such as + * address space handlers and notify handlers. The object is a constant + * size in order to allow them to be cached and reused. + * + * All variants of the ACPI_OBJECT_INTERNAL are defined with the same + * sequence of field types, with fields that are not used in a particular + * variant being named "Reserved". This is not strictly necessary, but + * may in some circumstances simplify understanding if these structures + * need to be displayed in a debugger having limited (or no) support for + * union types. It also simplifies some debug code in Dump_table() which + * dumps multi-level values: fetching Buffer.Pointer suffices to pick up + * the value or next level for any of several types. + */ + +/****************************************************************************** + * + * Common Descriptors + * + *****************************************************************************/ + +/* + * Common area for all objects. + * + * Data_type is used to differentiate between internal descriptors, and MUST + * be the first byte in this structure. + */ + + +#define ACPI_OBJECT_COMMON_HEADER /* Two 32-bit fields */\ + u8 data_type; /* To differentiate various internal objs */\ + u8 type; /* ACPI_OBJECT_TYPE */\ + u8 size; /* Size of entire descriptor */\ + u8 flags;\ + u16 reference_count; /* For object deletion management */\ + u16 acpi_cm_fill2;\ + union acpi_obj_internal *next; \ + +/* Defines for flag byte above */ + +#define AO_STATIC_ALLOCATION 0x1 + + +/* + * Common bitfield for the field objects + */ +#define ACPI_COMMON_FIELD_INFO /* Three 32-bit values */\ + u32 offset; /* Byte offset within containing object */\ + u16 length; /* # of bits in buffer */ \ + u8 granularity;\ + u8 bit_offset; /* Bit offset within min read/write data unit */\ + u8 access; /* Access_type */\ + u8 lock_rule;\ + u8 update_rule;\ + u8 access_attribute; + + +/****************************************************************************** + * + * Individual Object Descriptors + * + *****************************************************************************/ + + +typedef struct /* COMMON */ +{ + ACPI_OBJECT_COMMON_HEADER + UCHAR first_non_common_byte; + +} ACPI_OBJECT_COMMON; + + +typedef struct /* NUMBER - has value */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 value; + u32 reserved2; + u32 reserved3; + u32 reserved4; + + void *reserved_p1; + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_NUMBER; + + +typedef struct /* STRING - has length and pointer */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 length; /* # of bytes in string, excluding trailing null */ + u32 reserved2; + u32 reserved3; + u32 reserved4; + + char *pointer; /* String value in AML stream or in allocated space */ + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_STRING; + + +typedef struct /* BUFFER - has length, sequence, and pointer */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 length; /* # of bytes in buffer */ + u32 sequence; /* Sequential count of buffers created */ + u32 reserved3; + u32 reserved4; + + u8 *pointer; /* points to the buffer in allocated space */ + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_BUFFER; + + +typedef struct /* PACKAGE - has count, elements, next element */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 count; /* # of elements in package */ + u32 reserved2; + u32 reserved3; + u32 reserved4; + + union acpi_obj_internal **elements; /* Array of pointers to Acpi_objects */ + union acpi_obj_internal **next_element; /* used only while initializing */ + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_PACKAGE; + + +typedef struct /* FIELD UNIT */ +{ + ACPI_OBJECT_COMMON_HEADER + + ACPI_COMMON_FIELD_INFO + u32 sequence; /* Container's sequence number */ + + union acpi_obj_internal *container; /* Containing object (Buffer) */ + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_FIELD_UNIT; + + +typedef struct /* DEVICE - has handle and notification handler/context */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + + ACPI_HANDLE handle; + union acpi_obj_internal *sys_handler; /* Handler for system notifies */ + union acpi_obj_internal *drv_handler; /* Handler for driver notifies */ + union acpi_obj_internal *addr_handler; /* Handler for Address space */ + void *reserved_p5; + +} ACPI_OBJECT_DEVICE; + + +typedef struct /* EVENT */ +{ + ACPI_OBJECT_COMMON_HEADER + + u16 lock_count; + u16 thread_id; + u16 signal_count; + u16 fill1; + u32 reserved3; + u32 reserved4; + + void *semaphore; + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_EVENT; + + +#define INFINITE_CONCURRENCY 0xFF + +typedef struct /* METHOD */ +{ + ACPI_OBJECT_COMMON_HEADER + + u8 method_flags; + u8 param_count; + u8 concurrency; + u8 fill1; + u32 pcode_length; + u32 table_length; + ACPI_OWNER_ID owning_id; + u16 reserved4; + + u8 *pcode; + u8 *acpi_table; + void *parser_op; + void *semaphore; + void *reserved_p5; + +} ACPI_OBJECT_METHOD; + + +typedef struct /* MUTEX */ +{ + ACPI_OBJECT_COMMON_HEADER + + u16 lock_count; + u16 thread_id; + u16 sync_level; + u16 fill1; + u32 reserved3; + u32 reserved4; + + void *semaphore; + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_MUTEX; + +/* Flags for Region */ + +#define INITIAL_REGION_FLAGS 0x0000 /* value set when the region is created */ +#define REGION_AGRUMENT_DATA_VALID 0x0001 /* Addr/Len are set */ +#define REGION_INITIALIZED 0x0002 /* region init handler has been called */ + /* this includes _REG method, if any */ + +typedef struct /* REGION */ +{ + ACPI_OBJECT_COMMON_HEADER + + u16 space_id; + u16 region_flags; /* bits defined above */ + u32 address; + u32 length; + u32 reserved4; /* Region Specific data (PCI _ADR) */ + + union acpi_obj_internal *method; /* Associated control method */ + union acpi_obj_internal *addr_handler; /* Handler for system notifies */ + union acpi_obj_internal *link; /* Link in list of regions */ + /* list is owned by Addr_handler */ + ACPI_NAMED_OBJECT *REGmethod; /* _REG method for this region (if any) */ + ACPI_NAMED_OBJECT *nte; /* containing object */ + +} ACPI_OBJECT_REGION; + + +typedef struct /* POWER RESOURCE - has Handle and notification handler/context*/ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 system_level; + u32 resource_order; + u32 reserved3; + u32 reserved4; + + ACPI_HANDLE handle; + union acpi_obj_internal *sys_handler; /* Handler for system notifies */ + union acpi_obj_internal *drv_handler; /* Handler for driver notifies */ + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_POWER_RESOURCE; + + +typedef struct /* PROCESSOR - has Handle and notification handler/context*/ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 proc_id; + ACPI_IO_ADDRESS pblk_address; + u16 fill1; + u32 pblk_length; + u32 reserved4; + + ACPI_HANDLE handle; + union acpi_obj_internal *sys_handler; /* Handler for system notifies */ + union acpi_obj_internal *drv_handler; /* Handler for driver notifies */ + union acpi_obj_internal *addr_handler; /* Handler for Address space */ + void *reserved_p5; + +} ACPI_OBJECT_PROCESSOR; + + +typedef struct /* THERMAL ZONE - has Handle and Handler/Context */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + + ACPI_HANDLE handle; + union acpi_obj_internal *sys_handler; /* Handler for system notifies */ + union acpi_obj_internal *drv_handler; /* Handler for driver notifies */ + union acpi_obj_internal *addr_handler; /* Handler for Address space */ + void *reserved_p5; + +} ACPI_OBJECT_THERMAL_ZONE; + + +/* + * Internal types + */ + +typedef struct /* FIELD */ +{ + ACPI_OBJECT_COMMON_HEADER + + ACPI_COMMON_FIELD_INFO + u32 reserved4; + + union acpi_obj_internal *container; /* Containing object */ + void *reserved_p2; + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_FIELD; + + +typedef struct /* BANK FIELD */ +{ + ACPI_OBJECT_COMMON_HEADER + + ACPI_COMMON_FIELD_INFO + u32 value; /* Value to store into Bank_select */ + + ACPI_HANDLE bank_select; /* Bank select register */ + union acpi_obj_internal *container; /* Containing object */ + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_BANK_FIELD; + + +typedef struct /* INDEX FIELD */ +{ + /* + * No container pointer needed since the index and data register definitions + * will define how to access the respective registers + */ + ACPI_OBJECT_COMMON_HEADER + + ACPI_COMMON_FIELD_INFO + u32 value; /* Value to store into Index register */ + + ACPI_HANDLE index; /* Index register */ + ACPI_HANDLE data; /* Data register */ + void *reserved_p3; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_INDEX_FIELD; + + +typedef struct /* NOTIFY HANDLER */ +{ + ACPI_OBJECT_COMMON_HEADER + + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; + + ACPI_NAMED_OBJECT *nte; /* Parent device */ + NOTIFY_HANDLER handler; + void *context; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_NOTIFY_HANDLER; + + +/* Flags for address handler */ + +#define ADDR_HANDLER_DEFAULT_INSTALLED 0x1 + +typedef struct /* ADDRESS HANDLER */ +{ + ACPI_OBJECT_COMMON_HEADER + + u16 space_id; + u16 hflags; + ADDRESS_SPACE_HANDLER handler; + + ACPI_NAMED_OBJECT *nte; /* Parent device */ + void *context; + ADDRESS_SPACE_SETUP setup; + union acpi_obj_internal *link; /* Link to next handler on device */ + union acpi_obj_internal *region_list; /* regions using this handler */ + +} ACPI_OBJECT_ADDR_HANDLER; + + +/* + * The Reference object type is used for these opcodes: + * Arg[0-6], Local[0-7], Index_op, Name_op, Zero_op, One_op, Ones_op, Debug_op + */ + +typedef struct /* Reference - Local object type */ +{ + ACPI_OBJECT_COMMON_HEADER + + u16 op_code; + u8 fill1; + u8 target_type; /* Used for Index_op */ + u32 offset; /* Used for Arg_op, Local_op, and Index_op */ + u32 reserved3; + u32 reserved4; + + void *object; /* Name_op=>HANDLE to obj, Index_op=>ACPI_OBJECT_INTERNAL */ + ACPI_NAMED_OBJECT *nte; + union acpi_obj_internal **where; + void *reserved_p4; + void *reserved_p5; + +} ACPI_OBJECT_REFERENCE; + + +/****************************************************************************** + * + * ACPI_OBJECT_INTERNAL Descriptor - a giant union of all of the above + * + *****************************************************************************/ + +typedef union acpi_obj_internal +{ + ACPI_OBJECT_COMMON common; + ACPI_OBJECT_NUMBER number; + ACPI_OBJECT_STRING string; + ACPI_OBJECT_BUFFER buffer; + ACPI_OBJECT_PACKAGE package; + ACPI_OBJECT_FIELD_UNIT field_unit; + ACPI_OBJECT_DEVICE device; + ACPI_OBJECT_EVENT event; + ACPI_OBJECT_METHOD method; + ACPI_OBJECT_MUTEX mutex; + ACPI_OBJECT_REGION region; + ACPI_OBJECT_POWER_RESOURCE power_resource; + ACPI_OBJECT_PROCESSOR processor; + ACPI_OBJECT_THERMAL_ZONE thermal_zone; + ACPI_OBJECT_FIELD field; + ACPI_OBJECT_BANK_FIELD bank_field; + ACPI_OBJECT_INDEX_FIELD index_field; + ACPI_OBJECT_REFERENCE reference; + ACPI_OBJECT_NOTIFY_HANDLER notify_handler; + ACPI_OBJECT_ADDR_HANDLER addr_handler; + +} ACPI_OBJECT_INTERNAL; + +#endif /* _ACOBJECT_H */ diff --git a/drivers/acpi/include/acpi.h b/drivers/acpi/include/acpi.h new file mode 100644 index 000000000..5dc0a853a --- /dev/null +++ b/drivers/acpi/include/acpi.h @@ -0,0 +1,50 @@ + +/****************************************************************************** + * + * Name: acpi.h - Master include file, Publics and external data. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +/* + * Common includes for all ACPI driver files + * We put them here because we don't want to duplicate them + * in the rest of the source code again and again. + */ +#include "config.h" /* Configuration constants */ +#include "acenv.h" /* Target environment specific items */ +#include "actypes.h" /* Fundamental data types */ +#include "acexcep.h" /* Local exception codes */ +#include "macros.h" /* C macros */ +#include "actables.h" /* Acpi table definitions */ +#include "internal.h" /* Internal data types */ +#include "output.h" /* Error output and Debug macros */ +#include "acpiosxf.h" /* Interfaces to the Acpi-to-OS layer*/ +#include "acpixf.h" /* Acpi core external interfaces */ +#include "acobject.h" /* Acpi internal object */ +#include "globals.h" /* All global variables */ +#include "hardware.h" /* Hardware defines and interfaces */ +#include "common.h" /* Common (global) interfaces */ + + +#endif /* __ACPI_H__ */ diff --git a/drivers/acpi/include/acpiosxf.h b/drivers/acpi/include/acpiosxf.h new file mode 100644 index 000000000..6f5c30c56 --- /dev/null +++ b/drivers/acpi/include/acpiosxf.h @@ -0,0 +1,296 @@ + +/****************************************************************************** + * + * Name: acpiosd.h - All interfaces to the OS-dependent layer. These + * interfaces must be implemented by the OS-dependent + * front-end to the ACPI subsystem. + * + *****************************************************************************/ + + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACPIOSD_H__ +#define __ACPIOSD_H__ + +#include "acenv.h" +#include "actypes.h" + + +/* Priorities for Acpi_os_queue_for_execution */ + +#define OSD_PRIORITY_HIGH 1 +#define OSD_PRIORITY_MED 2 +#define OSD_PRIORITY_LO 3 +#define OSD_PRIORITY_GPE OSD_PRIORITY_HIGH + +#define ACPI_NO_UNIT_LIMIT ((u32) -1) +#define ACPI_MUTEX_SEM 1 + + +/* + * Types specific to the OS-dependent layer interfaces + */ + +typedef +u32 (*OSD_HANDLER) ( + void *context); + +typedef +void (*OSD_EXECUTION_CALLBACK) ( + void *context); + + +/* + * Initialization and shutdown primitives (Optional) + */ + +ACPI_STATUS +acpi_os_initialize ( + void); + +ACPI_STATUS +acpi_os_terminate ( + void); + +/* + * Synchronization primitives + */ + +ACPI_STATUS +acpi_os_create_semaphore ( + u32 max_units, + u32 initial_units, + ACPI_HANDLE *out_handle); + +ACPI_STATUS +acpi_os_delete_semaphore ( + ACPI_HANDLE handle); + +ACPI_STATUS +acpi_os_wait_semaphore ( + ACPI_HANDLE handle, + u32 units, + u32 timeout); + +ACPI_STATUS +acpi_os_signal_semaphore ( + ACPI_HANDLE handle, + u32 units); + +/* + * Memory allocation and mapping + */ + +void * +acpi_os_allocate ( + u32 size); + +void * +acpi_os_callocate ( + u32 size); + +void +acpi_os_free ( + void * memory); + +ACPI_STATUS +acpi_os_map_memory ( + void *physical_address, + u32 length, + void **logical_address); + +void +acpi_os_unmap_memory ( + void *logical_address, + u32 length); + + +/* + * Interrupt handlers + */ + +ACPI_STATUS +acpi_os_install_interrupt_handler ( + u32 interrupt_number, + OSD_HANDLER service_routine, + void *context); + +ACPI_STATUS +acpi_os_remove_interrupt_handler ( + u32 interrupt_number, + OSD_HANDLER service_routine); + + +/* + * Scheduling + */ + +ACPI_STATUS +acpi_os_queue_for_execution ( + u32 priority, + OSD_EXECUTION_CALLBACK function, + void *context); + +void +acpi_os_sleep ( + u32 seconds, + u32 milliseconds); + +void +acpi_os_sleep_usec ( + u32 microseconds); + +/* + * Platform/Hardware independent I/O interfaces + */ + +u8 +acpi_os_in8 ( + ACPI_IO_ADDRESS in_port); + + +u16 +acpi_os_in16 ( + ACPI_IO_ADDRESS in_port); + +u32 +acpi_os_in32 ( + ACPI_IO_ADDRESS in_port); + +void +acpi_os_out8 ( + ACPI_IO_ADDRESS out_port, + u8 value); + +void +acpi_os_out16 ( + ACPI_IO_ADDRESS out_port, + u16 value); + +void +acpi_os_out32 ( + ACPI_IO_ADDRESS out_port, + u32 value); + + +/* + * Standard access to PCI configuration space + */ + +ACPI_STATUS +acpi_os_read_pci_cfg_byte ( + u32 bus, + u32 device_function, + u32 register, + u8 *value); + +ACPI_STATUS +acpi_os_read_pci_cfg_word ( + u32 bus, + u32 device_function, + u32 register, + u16 *value); + +ACPI_STATUS +acpi_os_read_pci_cfg_dword ( + u32 bus, + u32 device_function, + u32 register, + u32 *value); + +ACPI_STATUS +acpi_os_write_pci_cfg_byte ( + u32 bus, + u32 device_function, + u32 register, + u8 value); + +ACPI_STATUS +acpi_os_write_pci_cfg_word ( + u32 bus, + u32 device_function, + u32 register, + u16 value); + + +ACPI_STATUS +acpi_os_write_pci_cfg_dword ( + u32 bus, + u32 device_function, + u32 register, + u32 value); + + +/* + * Miscellaneous + */ + +ACPI_STATUS +acpi_os_breakpoint ( + char *message); + +u8 +acpi_os_readable ( + void *pointer, + u32 length); + + +u8 +acpi_os_writable ( + void *pointer, + u32 length); + + +/* + * Debug print routines + */ + +s32 +acpi_os_printf ( + const char *format, + ...); + +s32 +acpi_os_vprintf ( + const char *format, + va_list args); + +/* + * Debug input + */ + +u32 +acpi_os_get_line ( + char *buffer); + + +/* + * Debug + */ + +void +acpi_os_dbg_assert( + void *failed_assertion, + void *file_name, + u32 line_number, + char *message); + + +#endif /* __ACPIOSD_H__ */ diff --git a/drivers/acpi/include/acpixf.h b/drivers/acpi/include/acpixf.h new file mode 100644 index 000000000..cc2205b63 --- /dev/null +++ b/drivers/acpi/include/acpixf.h @@ -0,0 +1,299 @@ + +/****************************************************************************** + * + * Name: acxface.h - External interfaces to the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __ACXFACE_H__ +#define __ACXFACE_H__ + +#include "actypes.h" +#include "actables.h" + +/* + * Global interfaces + */ + +ACPI_STATUS +acpi_initialize ( + ACPI_INIT_DATA *init_data); + +ACPI_STATUS +acpi_terminate ( + void); + +ACPI_STATUS +acpi_enable ( + void); + +ACPI_STATUS +acpi_disable ( + void); + +ACPI_STATUS +acpi_get_system_info( + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_format_exception ( + ACPI_STATUS exception, + ACPI_BUFFER *out_buffer); + + +/* + * ACPI table manipulation interfaces + */ + +ACPI_STATUS +acpi_load_firmware_tables ( + void); + +ACPI_STATUS +acpi_load_table ( + ACPI_TABLE_HEADER *table_ptr); + +ACPI_STATUS +acpi_unload_table ( + ACPI_TABLE_TYPE table_type); + +ACPI_STATUS +acpi_get_table_header ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_TABLE_HEADER *out_table_header); + +ACPI_STATUS +acpi_get_table ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_BUFFER *ret_buffer); + + +/* + * Namespace and name interfaces + */ + +ACPI_STATUS +acpi_load_namespace ( + void); + +ACPI_STATUS +acpi_walk_namespace ( + ACPI_OBJECT_TYPE type, + ACPI_HANDLE start_object, + u32 max_depth, + WALK_CALLBACK user_function, + void *context, + void * *return_value); + +ACPI_STATUS +acpi_get_name ( + ACPI_HANDLE handle, + u32 name_type, + ACPI_BUFFER *ret_path_ptr); + +ACPI_STATUS +acpi_get_handle ( + ACPI_HANDLE parent, + ACPI_STRING pathname, + ACPI_HANDLE *ret_handle); + + +/* + * Object manipulation and enumeration + */ + +ACPI_STATUS +acpi_evaluate_object ( + ACPI_HANDLE object, + ACPI_STRING pathname, + ACPI_OBJECT_LIST *parameter_objects, + ACPI_BUFFER *return_object_buffer); + +ACPI_STATUS +acpi_get_object_info ( + ACPI_HANDLE device, + ACPI_DEVICE_INFO *info); + +ACPI_STATUS +acpi_get_next_object ( + ACPI_OBJECT_TYPE type, + ACPI_HANDLE parent, + ACPI_HANDLE child, + ACPI_HANDLE *out_handle); + +ACPI_STATUS +acpi_get_type ( + ACPI_HANDLE object, + ACPI_OBJECT_TYPE *out_type); + +ACPI_STATUS +acpi_get_parent ( + ACPI_HANDLE object, + ACPI_HANDLE *out_handle); + + +/* + * Acpi_event handler interfaces + */ + +ACPI_STATUS +acpi_install_fixed_event_handler ( + u32 acpi_event, + FIXED_EVENT_HANDLER handler, + void *context); + +ACPI_STATUS +acpi_remove_fixed_event_handler ( + u32 acpi_event, + FIXED_EVENT_HANDLER handler); + +ACPI_STATUS +acpi_install_notify_handler ( + ACPI_HANDLE device, + u32 handler_type, + NOTIFY_HANDLER handler, + void *context); + +ACPI_STATUS +acpi_remove_notify_handler ( + ACPI_HANDLE device, + u32 handler_type, + NOTIFY_HANDLER handler); + +ACPI_STATUS +acpi_install_address_space_handler ( + ACPI_HANDLE device, + ACPI_ADDRESS_SPACE_TYPE space_id, + ADDRESS_SPACE_HANDLER handler, + ADDRESS_SPACE_SETUP setup, + void *context); + +ACPI_STATUS +acpi_remove_address_space_handler ( + ACPI_HANDLE device, + ACPI_ADDRESS_SPACE_TYPE space_id, + ADDRESS_SPACE_HANDLER handler); + +ACPI_STATUS +acpi_install_gpe_handler ( + u32 gpe_number, + u32 type, + GPE_HANDLER handler, + void *context); + +ACPI_STATUS +acpi_remove_gpe_handler ( + u32 gpe_number, + GPE_HANDLER handler); + +ACPI_STATUS +acpi_enable_event ( + u32 acpi_event, + u32 type); + +ACPI_STATUS +acpi_disable_event ( + u32 acpi_event, + u32 type); + +ACPI_STATUS +acpi_clear_event ( + u32 acpi_event, + u32 type); + +ACPI_STATUS +acpi_get_event_status ( + u32 acpi_event, + u32 type, + ACPI_EVENT_STATUS *event_status); + +/* + * Resource interfaces + */ + +ACPI_STATUS +acpi_get_current_resources( + ACPI_HANDLE device_handle, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_get_possible_resources( + ACPI_HANDLE device_handle, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_set_current_resources ( + ACPI_HANDLE device_handle, + ACPI_BUFFER *in_buffer); + +ACPI_STATUS +acpi_get_irq_routing_table ( + ACPI_HANDLE bus_device_handle, + ACPI_BUFFER *ret_buffer); + + +/* + * Hardware (ACPI device) interfaces + */ + +ACPI_STATUS +acpi_set_firmware_waking_vector ( + void *physical_address); + +ACPI_STATUS +acpi_get_firmware_waking_vector ( + void **physical_address); + +ACPI_STATUS +acpi_get_processor_throttling_info ( + ACPI_HANDLE processor_handle, + ACPI_BUFFER *user_buffer); + +ACPI_STATUS +acpi_set_processor_throttling_state ( + ACPI_HANDLE processor_handle, + u32 throttle_state); + +ACPI_STATUS +acpi_get_processor_throttling_state ( + ACPI_HANDLE processor_handle, + u32 *throttle_state); + +ACPI_STATUS +acpi_get_processor_cx_info ( + ACPI_HANDLE processor_handle, + ACPI_BUFFER *user_buffer); + +ACPI_STATUS +acpi_set_processor_sleep_state ( + ACPI_HANDLE processor_handle, + u32 cx_state); + +ACPI_STATUS +acpi_processor_sleep ( + ACPI_HANDLE processor_handle, + u32 *pm_timer_ticks); + + +#endif /* __ACXFACE_H__ */ diff --git a/drivers/acpi/include/actables.h b/drivers/acpi/include/actables.h new file mode 100644 index 000000000..67d2d84bb --- /dev/null +++ b/drivers/acpi/include/actables.h @@ -0,0 +1,188 @@ + +/****************************************************************************** + * + * Name: actables.h - Table data structures defined in ACPI specification + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACTABLES_H__ +#define __ACTABLES_H__ + + +/* + * Values for description table header signatures + */ + +#define RSDP_SIG "RSD PTR " /* RSDT Pointer signature */ +#define APIC_SIG "APIC" /* Multiple APIC Description Table */ +#define DSDT_SIG "DSDT" /* Differentiated System Description Table */ +#define FACP_SIG "FACP" /* Fixed ACPI Description Table */ +#define FACS_SIG "FACS" /* Firmware ACPI Control Structure */ +#define PSDT_SIG "PSDT" /* Persistent System Description Table */ +#define RSDT_SIG "RSDT" /* Root System Description Table */ +#define SSDT_SIG "SSDT" /* Secondary System Description Table */ +#define SBST_SIG "SBST" /* Smart Battery Specification Table */ +#define BOOT_SIG "BOOT" /* Boot table */ + + +#define GL_OWNED 0x02 /* Ownership of global lock is bit 1 */ + +/* values of Mapic.Model */ + +#define DUAL_PIC 0 +#define MULTIPLE_APIC 1 + +/* values of Type in APIC_HEADER */ + +#define APIC_PROC 0 +#define APIC_IO 1 + + +/* + * Architecture-independent tables + * The architecture dependent tables are in separate files + */ + +typedef struct /* Root System Descriptor Pointer */ +{ + char signature [8]; /* contains "RSD PTR " */ + u8 checksum; /* to make sum of struct == 0 */ + char oem_id [6]; /* OEM identification */ + u8 reserved; /* reserved - must be zero */ + u32 rsdt_physical_address; /* physical address of RSDT */ + +} ROOT_SYSTEM_DESCRIPTOR_POINTER; + + +typedef struct /* ACPI common table header */ +{ + char signature [4]; /* identifies type of table */ + u32 length; /* length of table, in bytes, + * including header */ + u8 revision; /* specification minor version # */ + u8 checksum; /* to make sum of entire table == 0 */ + char oem_id [6]; /* OEM identification */ + char oem_table_id [8]; /* OEM table identification */ + u32 oem_revision; /* OEM revision number */ + char asl_compiler_id [4]; /* ASL compiler vendor ID */ + u32 asl_compiler_revision; /* ASL compiler revision number */ + +} ACPI_TABLE_HEADER; + + +typedef struct /* APIC Table */ +{ + ACPI_TABLE_HEADER header; /* table header */ + u32 local_apic_address; /* Physical address for accessing local APICs */ + u32 PCATcompat : 1; /* a one indicates system also has dual 8259s */ + u32 reserved1 : 31; + +} APIC_TABLE; + + +typedef struct /* APIC Header */ +{ + u8 type; /* APIC type. Either APIC_PROC or APIC_IO */ + u8 length; /* Length of APIC structure */ + +} APIC_HEADER; + + +typedef struct /* Processor APIC */ +{ + APIC_HEADER header; + u8 processor_apic_id; /* ACPI processor id */ + u8 local_apic_id; /* processor's local APIC id */ + u32 processor_enabled: 1; /* Processor is usable if set */ + u32 reserved1 : 32; + +} PROCESSOR_APIC; + + +typedef struct /* IO APIC */ +{ + APIC_HEADER header; + u8 io_apic_id; /* I/O APIC ID */ + u8 reserved; /* reserved - must be zero */ + u32 io_apic_address; /* APIC's physical address */ + u32 vector; /* interrupt vector index where INTI + * lines start */ +} IO_APIC; + + +/* +** IA64 TODO: Add SAPIC Tables +*/ + +/* +** IA64 TODO: Modify Smart Battery Description to comply with ACPI IA64 +** extensions. +*/ +typedef struct /* Smart Battery Description Table */ +{ + ACPI_TABLE_HEADER header; + u32 warning_level; + u32 low_level; + u32 critical_level; + +} SMART_BATTERY_DESCRIPTION_TABLE; + + +/* + * ACPI Table information. We save the table address, length, + * and type of memory allocation (mapped or allocated) for each + * table for 1) when we exit, and 2) if a new table is installed + */ + +#define ACPI_MEM_NOT_ALLOCATED 0 +#define ACPI_MEM_ALLOCATED 1 +#define ACPI_MEM_MAPPED 2 + +#define ACPI_TABLE_SINGLE 0 +#define ACPI_TABLE_MULTIPLE 1 + + +/* Data about each known table type */ + +typedef struct _acpi_table_support +{ + char *name; + char *signature; + u8 sig_length; + u8 flags; + u16 status; + void **global_ptr; + +} ACPI_TABLE_SUPPORT; + + +/* + * Get the architecture-specific tables + */ + +#ifdef IA64 +#include "actbl64.h" +#else +#include "actbl32.h" +#endif + + +#endif /* __ACTABLES_H__ */ diff --git a/drivers/acpi/include/actbl32.h b/drivers/acpi/include/actbl32.h new file mode 100644 index 000000000..50e92fc30 --- /dev/null +++ b/drivers/acpi/include/actbl32.h @@ -0,0 +1,114 @@ +/****************************************************************************** + * + * Name: actbl32.h - ACPI tables specific to IA32 + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACTBL32_H__ +#define __ACTBL32_H__ + + +/* IA32 Root System Description Table */ + +typedef struct +{ + ACPI_TABLE_HEADER header; /* Table header */ + void *table_offset_entry [1]; /* Array of pointers to other */ + /* tables' headers */ +} ROOT_SYSTEM_DESCRIPTION_TABLE; + + +/* IA32 Firmware ACPI Control Structure */ + +typedef struct +{ + char signature[4]; /* signature "FACS" */ + u32 length; /* length of structure, in bytes */ + u32 hardware_signature; /* hardware configuration signature */ + u32 firmware_waking_vector; /* ACPI OS waking vector */ + u32 global_lock; /* Global Lock */ + u32 S4_bios_f : 1; /* Indicates if S4_bIOS support is present */ + u32 reserved1 : 31; /* must be 0 */ + u8 resverved3 [40]; /* reserved - must be zero */ + +} FIRMWARE_ACPI_CONTROL_STRUCTURE; + + +/* IA32 Fixed ACPI Description Table */ + +typedef struct +{ + ACPI_TABLE_HEADER header; /* table header */ + ACPI_TBLPTR firmware_ctrl; /* Physical address of FACS */ + ACPI_TBLPTR dsdt; /* Physical address of DSDT */ + u8 model; /* System Interrupt Model */ + u8 reserved1; /* reserved */ + u16 sci_int; /* System vector of SCI interrupt */ + ACPI_IO_ADDRESS smi_cmd; /* Port address of SMI command port */ + u8 acpi_enable; /* value to write to smi_cmd to enable ACPI */ + u8 acpi_disable; /* value to write to smi_cmd to disable ACPI */ + u8 S4_bios_req; /* Value to write to SMI CMD to enter S4_bIOS state */ + u8 reserved2; /* reserved - must be zero */ + ACPI_IO_ADDRESS pm1a_evt_blk; /* Port address of Power Mgt 1a Acpi_event Reg Blk */ + ACPI_IO_ADDRESS pm1b_evt_blk; /* Port address of Power Mgt 1b Acpi_event Reg Blk */ + ACPI_IO_ADDRESS pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + ACPI_IO_ADDRESS pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + ACPI_IO_ADDRESS pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + ACPI_IO_ADDRESS pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + ACPI_IO_ADDRESS gpe0blk; /* Port addr of General Purpose Acpi_event 0 Reg Blk */ + ACPI_IO_ADDRESS gpe1_blk; /* Port addr of General Purpose Acpi_event 1 Reg Blk */ + u8 pm1_evt_len; /* Byte Length of ports at pm1_x_evt_blk */ + u8 pm1_cnt_len; /* Byte Length of ports at pm1_x_cnt_blk */ + u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + u8 pm_tm_len; /* Byte Length of ports at pm_tm_blk */ + u8 gpe0blk_len; /* Byte Length of ports at gpe0_blk */ + u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + u8 gpe1_base; /* offset in gpe model where gpe1 events start */ + u8 reserved3; /* reserved */ + u16 plvl2_lat; /* worst case HW latency to enter/exit C2 state */ + u16 plvl3_lat; /* worst case HW latency to enter/exit C3 state */ + u16 flush_size; /* Size of area read to flush caches */ + u16 flush_stride; /* Stride used in flushing caches */ + u8 duty_offset; /* bit location of duty cycle field in p_cnt reg */ + u8 duty_width; /* bit width of duty cycle field in p_cnt reg */ + u8 day_alrm; /* index to day-of-month alarm in RTC CMOS RAM */ + u8 mon_alrm; /* index to month-of-year alarm in RTC CMOS RAM */ + u8 century; /* index to century in RTC CMOS RAM */ + u8 reserved4; /* reserved */ + u8 reserved4a; /* reserved */ + u8 reserved4b; /* reserved */ + u32 wb_invd : 1; /* wbinvd instruction works properly */ + u32 wb_invd_flush : 1; /* wbinvd flushes but does not invalidate */ + u32 proc_c1 : 1; /* all processors support C1 state */ + u32 plvl2_up : 1; /* C2 state works on MP system */ + u32 pwr_button : 1; /* Power button is handled as a generic feature */ + u32 sleep_button : 1; /* Sleep button is handled as a generic feature, or not present */ + u32 fixed_rTC : 1; /* RTC wakeup stat not in fixed register space */ + u32 rtcs4 : 1; /* RTC wakeup stat not possible from S4 */ + u32 tmr_val_ext : 1; /* tmr_val is 32 bits */ + u32 reserved5 : 23; /* reserved - must be zero */ + +} FIXED_ACPI_DESCRIPTION_TABLE; + + +#endif /* __ACTBL32_H__ */ + + diff --git a/drivers/acpi/include/actbl64.h b/drivers/acpi/include/actbl64.h new file mode 100644 index 000000000..f2a331349 --- /dev/null +++ b/drivers/acpi/include/actbl64.h @@ -0,0 +1,115 @@ + +/****************************************************************************** + * + * Name: actbl64.h - ACPI tables specific to IA64 + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ACTBL64_H__ +#define __ACTBL64_H__ + + +typedef UINT64 IO_ADDRESS; /* Only for clarity in declarations */ + + +/* IA64 Root System Description Table */ + +typedef struct +{ + ACPI_TABLE_HEADER header; /* Table header */ + u32 reserved_pad; /* IA64 alignment, must be 0 */ + void *table_offset_entry [1]; /* Array of pointers to other */ + /* tables' headers */ +} ROOT_SYSTEM_DESCRIPTION_TABLE; + + +/* IA64 Firmware ACPI Control Structure */ + +typedef struct +{ + char signature[4]; /* signature "FACS" */ + u32 length; /* length of structure, in bytes */ + u32 hardware_signature; /* hardware configuration signature */ + u32 reserved4; /* must be 0 */ + UINT64 firmware_waking_vector; /* ACPI OS waking vector */ + UINT64 global_lock; /* Global Lock */ + u32 S4_bios_f : 1; /* Indicates if S4_bIOS support is present */ + u32 reserved1 : 31; /* must be 0 */ + u8 resverved3 [28]; /* reserved - must be zero */ + +} FIRMWARE_ACPI_CONTROL_STRUCTURE; + + +/* IA64 Fixed ACPI Description Table */ + +typedef struct +{ + ACPI_TABLE_HEADER header; /* table header */ + u32 reserved_pad; /* IA64 alignment, must be 0 */ + ACPI_TBLPTR firmware_ctrl; /* Physical address of FACS */ + ACPI_TBLPTR acpi_dsdt; /* Physical address of DSDT */ + u8 model; /* System Interrupt Model */ + u8 address_space; /* Address Space Bitmask */ + u16 sci_int; /* System vector of SCI interrupt */ + u8 acpi_enable; /* value to write to smi_cmd to enable ACPI */ + u8 acpi_disable; /* value to write to smi_cmd to disable ACPI */ + u8 S4_bios_req; /* Value to write to SMI CMD to enter S4_bIOS state */ + u8 reserved2; /* reserved - must be zero */ + UINT64 smi_cmd; /* Port address of SMI command port */ + UINT64 pm1a_evt_blk; /* Port address of Power Mgt 1a Acpi_event Reg Blk */ + UINT64 pm1b_evt_blk; /* Port address of Power Mgt 1b Acpi_event Reg Blk */ + UINT64 pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + UINT64 pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + UINT64 pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + UINT64 pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + UINT64 gpe0blk; /* Port addr of General Purpose Acpi_event 0 Reg Blk */ + UINT64 gpe1_blk; /* Port addr of General Purpose Acpi_event 1 Reg Blk */ + u8 pm1_evt_len; /* Byte Length of ports at pm1_x_evt_blk */ + u8 pm1_cnt_len; /* Byte Length of ports at pm1_x_cnt_blk */ + u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + u8 pm_tm_len; /* Byte Length of ports at pm_tm_blk */ + u8 gpe0blk_len; /* Byte Length of ports at gpe0_blk */ + u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + u8 gpe1_base; /* offset in gpe model where gpe1 events start */ + u8 reserved3; /* reserved */ + u16 Plvl2_lat; /* worst case HW latency to enter/exit C2 state */ + u16 Plvl3_lat; /* worst case HW latency to enter/exit C3 state */ + u8 day_alrm; /* index to day-of-month alarm in RTC CMOS RAM */ + u8 mon_alrm; /* index to month-of-year alarm in RTC CMOS RAM */ + u8 century; /* index to century in RTC CMOS RAM */ + u8 reserved4; /* reserved */ + u32 flush_cash : 1; /* PAL_FLUSH_CACHE is correctly supported */ + u32 reserved5 : 1; /* reserved - must be zero */ + u32 proc_c1 : 1; /* all processors support C1 state */ + u32 Plvl2_up : 1; /* C2 state works on MP system */ + u32 pwr_button : 1; /* Power button is handled as a generic feature */ + u32 sleep_button : 1; /* Sleep button is handled as a generic feature, or not present */ + u32 fixed_rTC : 1; /* RTC wakeup stat not in fixed register space */ + u32 RTCS4 : 1; /* RTC wakeup stat not possible from S4 */ + u32 tmr_val_ext : 1; /* tmr_val is 32 bits */ + u32 dock_cap : 1; /* Supports Docking */ + u32 reserved6 : 22; /* reserved - must be zero */ + +} FIXED_ACPI_DESCRIPTION_TABLE; + + +#endif /* __ACTBL64_H__ */ + diff --git a/drivers/acpi/include/actypes.h b/drivers/acpi/include/actypes.h new file mode 100644 index 000000000..669bb3b7f --- /dev/null +++ b/drivers/acpi/include/actypes.h @@ -0,0 +1,970 @@ + +/****************************************************************************** + * + * Name: actypes.h - Common data types for the entire ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ACTYPES_H +#define _ACTYPES_H + +/*! [Begin] no source code translation (keep the typedefs) */ + +/* + * Data types - Fixed across all compilation models + * + * BOOLEAN Logical Boolean. 1 byte value containing a 0 for FALSE or a 1 for TRUE. Other values are undefined. + * INT8 8-bit (1 byte) signed value + * UINT8 8-bit (1 byte) unsigned value + * INT16 16-bit (2 byte) signed value + * UINT16 16-bit (2 byte) unsigned value + * INT32 32-bit (4 byte) signed value + * UINT32 32-bit (4 byte) unsigned value + * INT64 64-bit (8 byte) signed value + * UINT64 64-bit (8 byte) unsigned value + * NATIVE_INT 32-bit on IA-32, 64-bit on IA-64 signed value + * NATIVE_UINT 32-bit on IA-32, 64-bit on IA-64 unsigned value + * UCHAR Character. 1 byte unsigned value. + */ + +#ifdef _IA64 +/* + * 64-bit type definitions + */ +typedef signed char INT8; +typedef unsigned char UINT8; +typedef unsigned char UCHAR; +typedef short INT16; +typedef unsigned short UINT16; +typedef int INT32; +typedef unsigned int UINT32; +typedef long INT64; +typedef unsigned long UINT64; + +typedef UINT64 NATIVE_UINT; +typedef INT64 NATIVE_INT; + +typedef NATIVE_UINT ACPI_TBLPTR; +typedef UINT64 ACPI_IO_ADDRESS; + +#define ALIGNED_ADDRESS_BOUNDARY 0x00000008 + +/* (No hardware alignment support in IA64) */ + + +#elif _IA16 +/* + * 16-bit type definitions + */ +typedef signed char INT8; +typedef unsigned char UINT8; +typedef unsigned char UCHAR; +typedef int INT16; +typedef unsigned int UINT16; +typedef long INT32; +typedef unsigned long UINT32; + +typedef UINT16 NATIVE_UINT; +typedef INT16 NATIVE_INT; + +typedef UINT32 ACPI_TBLPTR; +typedef UINT32 ACPI_IO_ADDRESS; + +#define ALIGNED_ADDRESS_BOUNDARY 0x00000002 +#define _HW_ALIGNMENT_SUPPORT + + +#else +/* + * 32-bit type definitions (default) + */ +typedef signed char INT8; +typedef unsigned char UINT8; +typedef unsigned char UCHAR; +typedef short INT16; +typedef unsigned short UINT16; +typedef int INT32; +typedef unsigned int UINT32; + +typedef UINT32 NATIVE_UINT; +typedef INT32 NATIVE_INT; + +typedef NATIVE_UINT ACPI_TBLPTR; +typedef UINT32 ACPI_IO_ADDRESS; + +#define ALIGNED_ADDRESS_BOUNDARY 0x00000004 +#define _HW_ALIGNMENT_SUPPORT + + +#endif + + + +/* + * Miscellaneous common types + */ + +typedef UINT8 BOOLEAN; +typedef UINT32 UINT32_BIT; +typedef NATIVE_INT ACPI_PTRDIFF; +typedef NATIVE_UINT ACPI_SIZE; + + +/* + * Data type ranges + */ + +#define ACPI_UCHAR_MAX (UCHAR) 0xFF +#define ACPI_INT32_MAX (INT32) 0x7FFFFFFF +#define ACPI_UINT32_MAX (UINT32) 0xFFFFFFFF + + +#ifdef DEFINE_ALTERNATE_TYPES +/* + * Types used only in translated source + */ +typedef INT8 s8; +typedef INT16 s16; +typedef INT32 s32; +typedef UINT8 u8; +typedef UINT16 u16; +typedef UINT32 u32; +#endif + +/*! [End] no source code translation !*/ + + +/* + * Useful defines + */ + +#ifdef FALSE +#undef FALSE +#endif +#define FALSE (1 == 0) + +#ifdef TRUE +#undef TRUE +#endif +#define TRUE (1 == 1) + +#ifndef NULL +#define NULL (void *) 0 +#endif + + +/* + * Local datatypes + */ + +typedef u32 ACPI_STATUS; /* All ACPI Exceptions */ +typedef u32 ACPI_NAME; /* 4-char ACPI name */ +typedef char* ACPI_STRING; /* Null terminated ASCII string */ +typedef void* ACPI_HANDLE; /* Actually a ptr to an NTE */ + + +/* + * Constants with special meanings + */ + +#define ACPI_ROOT_OBJECT (ACPI_HANDLE)(-1) + + +/* + * Sleep state constants + */ +#define ACPI_STATE_S0 (u8) 0 +#define ACPI_STATE_S1 (u8) 1 +#define ACPI_STATE_S2 (u8) 2 +#define ACPI_STATE_S3 (u8) 3 +#define ACPI_STATE_S4 (u8) 4 +#define ACPI_STATE_S4_bIOS (u8) 5 +#define ACPI_STATE_S5 (u8) 6 +#define ACPI_S_STATES_MAX ACPI_STATE_S5 + + +/* + * Table types. These values are passed to the table related APIs + */ + +typedef u32 ACPI_TABLE_TYPE; + +#define ACPI_TABLE_RSDP (ACPI_TABLE_TYPE) 0 +#define ACPI_TABLE_APIC (ACPI_TABLE_TYPE) 1 +#define ACPI_TABLE_DSDT (ACPI_TABLE_TYPE) 2 +#define ACPI_TABLE_FACP (ACPI_TABLE_TYPE) 3 +#define ACPI_TABLE_FACS (ACPI_TABLE_TYPE) 4 +#define ACPI_TABLE_PSDT (ACPI_TABLE_TYPE) 5 +#define ACPI_TABLE_RSDT (ACPI_TABLE_TYPE) 6 +#define ACPI_TABLE_SSDT (ACPI_TABLE_TYPE) 7 +#define ACPI_TABLE_SBST (ACPI_TABLE_TYPE) 8 +#define ACPI_TABLE_BOOT (ACPI_TABLE_TYPE) 9 +#define ACPI_TABLE_MAX 9 +#define NUM_ACPI_TABLES 10 + + +/* + * Types associated with names. The first group of + * values correspond to the definition of the ACPI Object_type operator (See the ACPI Spec). + * Therefore, only add to the first group if the spec changes! + * + * Types must be kept in sync with the Acpi_ns_properties and Acpi_ns_type_names arrays + */ + +typedef u32 ACPI_OBJECT_TYPE; +typedef u8 OBJECT_TYPE_INTERNAL; + +#define ACPI_TYPE_ANY 0 /* 0x00 */ +#define ACPI_TYPE_NUMBER 1 /* 0x01 Byte/Word/Dword/Zero/One/Ones */ +#define ACPI_TYPE_STRING 2 /* 0x02 */ +#define ACPI_TYPE_BUFFER 3 /* 0x03 */ +#define ACPI_TYPE_PACKAGE 4 /* 0x04 Byte_const, multiple Data_term/Constant/Super_name */ +#define ACPI_TYPE_FIELD_UNIT 5 /* 0x05 */ +#define ACPI_TYPE_DEVICE 6 /* 0x06 Name, multiple Named_object */ +#define ACPI_TYPE_EVENT 7 /* 0x07 */ +#define ACPI_TYPE_METHOD 8 /* 0x08 Name, Byte_const, multiple Code */ +#define ACPI_TYPE_MUTEX 9 /* 0x09 */ +#define ACPI_TYPE_REGION 10 /* 0x0A */ +#define ACPI_TYPE_POWER 11 /* 0x0B Name,Byte_const,Word_const,multi Named_object */ +#define ACPI_TYPE_PROCESSOR 12 /* 0x0C Name,Byte_const,DWord_const,Byte_const,multi Nm_o */ +#define ACPI_TYPE_THERMAL 13 /* 0x0D Name, multiple Named_object */ +#define ACPI_TYPE_BUFFER_FIELD 14 /* 0x0E */ +#define ACPI_TYPE_DDB_HANDLE 15 /* 0x0F */ +#define ACPI_TYPE_DEBUG_OBJECT 16 /* 0x10 */ + +#define ACPI_TYPE_MAX 16 + +/* + * This section contains object types that do not relate to the ACPI Object_type operator. + * They are used for various internal purposes only. A numerical gap is provided in + * case additional "official" Object_types are added in the future. Also, values exceeding + * the largest official ACPI Object_type must not overlap with defined AML opcodes. + */ +#define INTERNAL_TYPE_BEGIN 25 +#define INTERNAL_TYPE_DEF_FIELD 25 /* 0x19 */ +#define INTERNAL_TYPE_BANK_FIELD 26 /* 0x1A */ +#define INTERNAL_TYPE_INDEX_FIELD 27 /* 0x1B */ +#define INTERNAL_TYPE_DEF_FIELD_DEFN 28 /* 0x1C Name, Byte_const, multiple Field_element */ +#define INTERNAL_TYPE_BANK_FIELD_DEFN 29 /* 0x1D 2 Name,DWord_const,Byte_const,multi Field_element */ +#define INTERNAL_TYPE_INDEX_FIELD_DEFN 30 /* 0x1E 2 Name, Byte_const, multiple Field_element */ +#define INTERNAL_TYPE_IF 31 /* 0x1F Op_code, multiple Code */ +#define INTERNAL_TYPE_ELSE 32 /* 0x20 multiple Code */ +#define INTERNAL_TYPE_WHILE 33 /* 0x21 Op_code, multiple Code */ +#define INTERNAL_TYPE_SCOPE 34 /* 0x22 Name, multiple Named_object */ +#define INTERNAL_TYPE_DEF_ANY 35 /* 0x23 type is Any, suppress search of enclosing scopes */ +#define INTERNAL_TYPE_REFERENCE 36 /* 0x24 Arg#, Local#, Name, Debug; used only in descriptors */ +#define INTERNAL_TYPE_ALIAS 37 /* 0x25 */ +#define INTERNAL_TYPE_NOTIFY 38 /* 0x26 */ +#define INTERNAL_TYPE_ADDRESS_HANDLER 39 /* 0x27 */ +#define INTERNAL_TYPE_METHOD_ARGUMENT 40 /* 0x28 */ +#define INTERNAL_TYPE_METHOD_LOCAL_VAR 41 /* 0x29 */ + +#define INTERNAL_TYPE_MAX 41 + +#define INTERNAL_TYPE_INVALID 42 +#define ACPI_TYPE_NOT_FOUND 0xFF + +/* + * Acpi_event Types: + * ------------ + * Fixed & general purpose... + */ + +typedef u32 ACPI_EVENT_TYPE; + +#define ACPI_EVENT_FIXED (ACPI_EVENT_TYPE) 0 +#define ACPI_EVENT_GPE (ACPI_EVENT_TYPE) 1 + +/* + * Fixed events + */ + +#define ACPI_EVENT_PMTIMER (ACPI_EVENT_TYPE) 0 + /* + * There's no bus master event so index 1 is used for IRQ's that are not + * handled by the SCI handler + */ +#define ACPI_EVENT_NOT_USED (ACPI_EVENT_TYPE) 1 +#define ACPI_EVENT_GLOBAL (ACPI_EVENT_TYPE) 2 +#define ACPI_EVENT_POWER_BUTTON (ACPI_EVENT_TYPE) 3 +#define ACPI_EVENT_SLEEP_BUTTON (ACPI_EVENT_TYPE) 4 +#define ACPI_EVENT_RTC (ACPI_EVENT_TYPE) 5 +#define ACPI_EVENT_GENERAL (ACPI_EVENT_TYPE) 6 +#define ACPI_EVENT_MAX 6 +#define NUM_FIXED_EVENTS (ACPI_EVENT_TYPE) 7 + +#define ACPI_GPE_INVALID 0xFF +#define ACPI_GPE_MAX 0xFF +#define NUM_GPE 256 + +#define ACPI_EVENT_LEVEL_TRIGGERED (ACPI_EVENT_TYPE) 1 +#define ACPI_EVENT_EDGE_TRIGGERED (ACPI_EVENT_TYPE) 2 + +/* + * Acpi_event Status: + * ------------- + * The encoding of ACPI_EVENT_STATUS is illustrated below. + * Note that a set bit (1) indicates the property is TRUE + * (e.g. if bit 0 is set then the event is enabled). + * +---------------+-+-+ + * | Bits 31:2 |1|0| + * +---------------+-+-+ + * | | | + * | | +- Enabled? + * | +--- Set? + * +----------- <Reserved> + */ +typedef u32 ACPI_EVENT_STATUS; + +#define ACPI_EVENT_FLAG_ENABLED (ACPI_EVENT_STATUS) 0x01 +#define ACPI_EVENT_FLAG_SET (ACPI_EVENT_STATUS) 0x02 + + +/* Notify types */ + +#define ACPI_SYSTEM_NOTIFY 0 +#define ACPI_DEVICE_NOTIFY 1 +#define ACPI_MAX_NOTIFY_HANDLER_TYPE 1 + +#define MAX_SYS_NOTIFY 0x7f + + +/* Address Space (Operation Region) Types */ + +typedef u32 ACPI_ADDRESS_SPACE_TYPE; + +#define ADDRESS_SPACE_SYSTEM_MEMORY (ACPI_ADDRESS_SPACE_TYPE) 0 +#define ADDRESS_SPACE_SYSTEM_IO (ACPI_ADDRESS_SPACE_TYPE) 1 +#define ADDRESS_SPACE_PCI_CONFIG (ACPI_ADDRESS_SPACE_TYPE) 2 +#define ADDRESS_SPACE_EC (ACPI_ADDRESS_SPACE_TYPE) 3 +#define ADDRESS_SPACE_SMBUS (ACPI_ADDRESS_SPACE_TYPE) 4 + + +/* + * External ACPI object definition + */ + +typedef union acpi_obj +{ + ACPI_OBJECT_TYPE type; /* See definition of Acpi_ns_type for values */ + struct + { + ACPI_OBJECT_TYPE type; + u32 value; /* The actual number */ + } number; + + struct + { + ACPI_OBJECT_TYPE type; + u32 length; /* # of bytes in string, excluding trailing null */ + char *pointer; /* points to the string value */ + } string; + + struct + { + ACPI_OBJECT_TYPE type; + u32 length; /* # of bytes in buffer */ + u8 *pointer; /* points to the buffer */ + } buffer; + + struct + { + ACPI_OBJECT_TYPE type; + u32 fill1; + ACPI_HANDLE handle; /* object reference */ + } reference; + + struct + { + ACPI_OBJECT_TYPE type; + u32 count; /* # of elements in package */ + union acpi_obj *elements; /* Pointer to an array of ACPI_OBJECTs */ + } package; + + struct + { + ACPI_OBJECT_TYPE type; + u32 proc_id; + u32 pblk_address; + u32 pblk_length; + } processor; + + struct + { + ACPI_OBJECT_TYPE type; + u32 system_level; + u32 resource_order; + } power_resource; + +} ACPI_OBJECT, *PACPI_OBJECT; + + +/* + * List of objects, used as a parameter list for control method evaluation + */ + +typedef struct acpi_obj_list +{ + u32 count; + ACPI_OBJECT *pointer; + +} ACPI_OBJECT_LIST, *PACPI_OBJECT_LIST; + + +/* + * Miscellaneous common Data Structures used by the interfaces + */ + +typedef struct +{ + u32 length; /* Length in bytes of the buffer */ + void *pointer; /* pointer to buffer */ + +} ACPI_BUFFER; + + +/* + * Name_type for Acpi_get_name + */ + +#define ACPI_FULL_PATHNAME 0 +#define ACPI_SINGLE_NAME 1 +#define ACPI_NAME_TYPE_MAX 1 + + +/* + * Structure and flags for Acpi_get_system_info + */ + +#define SYS_MODE_UNKNOWN 0x0000 +#define SYS_MODE_ACPI 0x0001 +#define SYS_MODE_LEGACY 0x0002 +#define SYS_MODES_MASK 0x0003 + +/* + * ACPI CPU Cx state handler + */ +typedef +ACPI_STATUS (*ACPI_SET_C_STATE_HANDLER) ( + NATIVE_UINT pblk_address); + +/* + * ACPI Cx State info + */ +typedef struct +{ + u32 state_number; + u32 latency; +} ACPI_CX_STATE; + +/* + * ACPI CPU throttling info + */ +typedef struct +{ + u32 state_number; + u32 percent_of_clock; +} ACPI_CPU_THROTTLING_STATE; + +/* + * ACPI Table Info. One per ACPI table _type_ + */ +typedef struct acpi_table_info +{ + u32 count; + +} ACPI_TABLE_INFO; + + +/* + * System info returned by Acpi_get_system_info() + */ + +typedef struct _acpi_sys_info +{ + u32 acpi_ca_version; + u32 flags; + u32 timer_resolution; + u32 reserved1; + u32 reserved2; + u32 debug_level; + u32 debug_layer; + u32 num_table_types; + ACPI_TABLE_INFO table_info [NUM_ACPI_TABLES]; + +} ACPI_SYSTEM_INFO; + + +/* + * System Initiailization data. This data is passed to ACPIInitialize + * copyied to global data and retained by ACPI CA + */ + +typedef struct _acpi_init_data +{ + void *RSDP_physical_address; /* Address of RSDP, needed it it is */ + /* not found in the IA32 manner */ +} ACPI_INIT_DATA; + +/* + * Various handlers and callback procedures + */ + +typedef +u32 (*FIXED_EVENT_HANDLER) ( + void *context); + +typedef +void (*GPE_HANDLER) ( + void *context); + +typedef +void (*NOTIFY_HANDLER) ( + ACPI_HANDLE device, + u32 value, + void *context); + +#define ADDRESS_SPACE_READ 1 +#define ADDRESS_SPACE_WRITE 2 + +typedef +ACPI_STATUS (*ADDRESS_SPACE_HANDLER) ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + +#define ACPI_DEFAULT_HANDLER ((ADDRESS_SPACE_HANDLER) NULL) + + +typedef +ACPI_STATUS (*ADDRESS_SPACE_SETUP) ( + ACPI_HANDLE region_handle, + u32 function, + void *handler_context, + void **return_context); + +#define ACPI_REGION_ACTIVATE 0 +#define ACPI_REGION_DEACTIVATE 1 + +typedef +ACPI_STATUS (*WALK_CALLBACK) ( + ACPI_HANDLE obj_handle, + u32 nesting_level, + void *context, + void **return_value); + + +/* Interrupt handler return values */ + +#define INTERRUPT_NOT_HANDLED 0x00 +#define INTERRUPT_HANDLED 0x01 + + +/* Structure and flags for Acpi_get_device_info */ + +#define ACPI_VALID_HID 0x1 +#define ACPI_VALID_UID 0x2 +#define ACPI_VALID_ADR 0x4 +#define ACPI_VALID_STA 0x8 + + +#define ACPI_COMMON_OBJ_INFO \ + ACPI_OBJECT_TYPE type; /* ACPI object type */\ + ACPI_NAME name; /* ACPI object Name */\ + /*\ + * TBD: [Restructure] Do we want or need these next two??\ + */\ + ACPI_HANDLE parent; /* Parent object */\ + ACPI_HANDLE children; /* Linked list of children */\ + u32 valid /* ????? */ + +typedef struct +{ + ACPI_COMMON_OBJ_INFO; +} ACPI_OBJ_INFO_HEADER; + + +typedef struct +{ + ACPI_COMMON_OBJ_INFO; + + /* + * TBD: [Restructure]: a HID or a _UID can return either a number or a string + */ + char hardware_id [9]; /* _HID value if any */ + char unique_id[9]; /* _UID value if any */ + u32 address; /* _ADR value if any */ + u32 current_status; /* _STA value */ +} ACPI_DEVICE_INFO; + + +/* Context structs for address space handlers */ + +typedef struct +{ + void *handler_context; + u32 seg; + u32 bus; + u32 dev_func; +} PCI_HANDLER_CONTEXT; + + +typedef struct +{ + void *handler_context; + char *mapped_physical_address; + char *mapped_logical_address; + u32 mapped_length; + +} MEM_HANDLER_CONTEXT; + + +/* + * C-state handler + */ + +typedef ACPI_STATUS (*ACPI_C_STATE_HANDLER) (ACPI_IO_ADDRESS, u32*); + + +/* + * Definitions for Resource Attributes + */ + +/* + * Memory Attributes + */ +#define READ_ONLY_MEMORY (u8) 0x00 +#define READ_WRITE_MEMORY (u8) 0x01 + +#define NON_CACHEABLE_MEMORY (u8) 0x00 +#define CACHABLE_MEMORY (u8) 0x01 +#define WRITE_COMBINING_MEMORY (u8) 0x02 +#define PREFETCHABLE_MEMORY (u8) 0x03 + +/* + * IO Attributes + * The ISA IO ranges are: n000-n0FFh, n400-n4_fFh, n800-n8_fFh, n_c00-n_cFFh. + * The non-ISA IO ranges are: n100-n3_fFh, n500-n7_fFh, n900-n_bFFh, n_cD0-n_fFFh. + */ +#define NON_ISA_ONLY_RANGES (u8) 0x01 +#define ISA_ONLY_RANGES (u8) 0x02 +#define ENTIRE_RANGE (NON_ISA_ONLY_RANGES | ISA_ONLY_RANGES) + +/* + * IO Port Descriptor Decode + */ +#define DECODE_10 (u8) 0x00 /* 10-bit IO address decode */ +#define DECODE_16 (u8) 0x01 /* 16-bit IO address decode */ + +/* + * IRQ Attributes + */ +#define EDGE_SENSITIVE (u8) 0x00 +#define LEVEL_SENSITIVE (u8) 0x01 + +#define ACTIVE_HIGH (u8) 0x00 +#define ACTIVE_LOW (u8) 0x01 + +#define EXCLUSIVE (u8) 0x00 +#define SHARED (u8) 0x01 + +/* + * DMA Attributes + */ +#define COMPATIBILITY (u8) 0x00 +#define TYPE_A (u8) 0x01 +#define TYPE_B (u8) 0x02 +#define TYPE_F (u8) 0x03 + +#define NOT_BUS_MASTER (u8) 0x00 +#define BUS_MASTER (u8) 0x01 + +#define TRANSFER_8 (u8) 0x00 +#define TRANSFER_8_16 (u8) 0x01 +#define TRANSFER_16 (u8) 0x02 + +/* + * Start Dependent Functions Priority definitions + */ +#define GOOD_CONFIGURATION (u8) 0x00 +#define ACCEPTABLE_CONFIGURATION (u8) 0x01 +#define SUB_OPTIMAL_CONFIGURATION (u8) 0x02 + +/* + * 16, 32 and 64-bit Address Descriptor resource types + */ +#define MEMORY_RANGE (u8) 0x00 +#define IO_RANGE (u8) 0x01 +#define BUS_NUMBER_RANGE (u8) 0x02 + +#define ADDRESS_NOT_FIXED (u8) 0x00 +#define ADDRESS_FIXED (u8) 0x01 + +#define POS_DECODE (u8) 0x00 +#define SUB_DECODE (u8) 0x01 + +#define PRODUCER (u8) 0x00 +#define CONSUMER (u8) 0x01 + + +/* + * Structures used to describe device resources + */ +typedef struct +{ + u32 edge_level; + u32 active_high_low; + u32 shared_exclusive; + u32 number_of_interrupts; + u32 interrupts[1]; + +} IRQ_RESOURCE; + +typedef struct +{ + u32 type; + u32 bus_master; + u32 transfer; + u32 number_of_channels; + u32 channels[1]; + +} DMA_RESOURCE; + +typedef struct +{ + u32 compatibility_priority; + u32 performance_robustness; + +} START_DEPENDENT_FUNCTIONS_RESOURCE; + +/* + * END_DEPENDENT_FUNCTIONS_RESOURCE struct is not + * needed because it has no fields + */ + +typedef struct +{ + u32 io_decode; + u32 min_base_address; + u32 max_base_address; + u32 alignment; + u32 range_length; + +} IO_RESOURCE; + +typedef struct +{ + u32 base_address; + u32 range_length; + +} FIXED_IO_RESOURCE; + +typedef struct +{ + u32 length; + u8 reserved[1]; + +} VENDOR_RESOURCE; + +typedef struct +{ + u32 read_write_attribute; + u32 min_base_address; + u32 max_base_address; + u32 alignment; + u32 range_length; + +} MEMORY24_RESOURCE; + +typedef struct +{ + u32 read_write_attribute; + u32 min_base_address; + u32 max_base_address; + u32 alignment; + u32 range_length; + +} MEMORY32_RESOURCE; + +typedef struct +{ + u32 read_write_attribute; + u32 range_base_address; + u32 range_length; + +} FIXED_MEMORY32_RESOURCE; + +typedef struct +{ + u16 cache_attribute; + u16 read_write_attribute; + +} MEMORY_ATTRIBUTE; + +typedef struct +{ + u16 range_attribute; + u16 reserved; + +} IO_ATTRIBUTE; + +typedef struct +{ + u16 reserved1; + u16 reserved2; + +} BUS_ATTRIBUTE; + +typedef union +{ + MEMORY_ATTRIBUTE memory; + IO_ATTRIBUTE io; + BUS_ATTRIBUTE bus; + +} ATTRIBUTE_DATA; + +typedef struct +{ + u32 resource_type; + u32 producer_consumer; + u32 decode; + u32 min_address_fixed; + u32 max_address_fixed; + ATTRIBUTE_DATA attribute; + u32 granularity; + u32 min_address_range; + u32 max_address_range; + u32 address_translation_offset; + u32 address_length; + u32 resource_source_index; + u32 resource_source_string_length; + u8 resource_source[1]; + +} ADDRESS16_RESOURCE; + +typedef struct +{ + u32 resource_type; + u32 producer_consumer; + u32 decode; + u32 min_address_fixed; + u32 max_address_fixed; + ATTRIBUTE_DATA attribute; + u32 granularity; + u32 min_address_range; + u32 max_address_range; + u32 address_translation_offset; + u32 address_length; + u32 resource_source_index; + u32 resource_source_string_length; + u8 resource_source[1]; + +} ADDRESS32_RESOURCE; + +typedef struct +{ + u32 producer_consumer; + u32 edge_level; + u32 active_high_low; + u32 shared_exclusive; + u32 number_of_interrupts; + u32 interrupts[1]; + u32 resource_source_index; + u32 resource_source_string_length; + u8 resource_source[1]; + +} EXTENDED_IRQ_RESOURCE; + +typedef enum +{ + irq, + dma, + start_dependent_functions, + end_dependent_functions, + io, + fixed_io, + vendor_specific, + end_tag, + memory24, + memory32, + fixed_memory32, + address16, + address32, + extended_irq +} RESOURCE_TYPE; + +typedef union +{ + IRQ_RESOURCE irq; + DMA_RESOURCE dma; + START_DEPENDENT_FUNCTIONS_RESOURCE start_dependent_functions; + IO_RESOURCE io; + FIXED_IO_RESOURCE fixed_io; + VENDOR_RESOURCE vendor_specific; + MEMORY24_RESOURCE memory24; + MEMORY32_RESOURCE memory32; + FIXED_MEMORY32_RESOURCE fixed_memory32; + ADDRESS16_RESOURCE address16; + ADDRESS32_RESOURCE address32; + EXTENDED_IRQ_RESOURCE extended_irq; +} RESOURCE_DATA; + +typedef struct _resource_tag +{ + RESOURCE_TYPE id; + u32 length; + RESOURCE_DATA data; +} RESOURCE; + +#define RESOURCE_LENGTH 12 +#define RESOURCE_LENGTH_NO_DATA 8 + +/* + * END: Definitions for Resource Attributes + */ + +/* + * Definitions for PCI Routing tables + */ +typedef struct +{ + u32 address; + u32 pin; + u32 source_index; + u8 source[1]; + +} PRT_ENTRY; + +typedef struct _prt_tag +{ + u32 length; + PRT_ENTRY data; + +} PCI_ROUTING_TABLE; + + +/* + * END: Definitions for PCI Routing tables + */ + +#endif /* ACTYPES_H */ diff --git a/drivers/acpi/include/amlcode.h b/drivers/acpi/include/amlcode.h new file mode 100644 index 000000000..464e0cd6b --- /dev/null +++ b/drivers/acpi/include/amlcode.h @@ -0,0 +1,452 @@ + +/****************************************************************************** + * + * Name: amlcode.h - Definitions for AML, as included in "definition blocks" + * Declarations and definitions contained herein are derived + * directly from the ACPI specification. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __AMLCODE_H__ +#define __AMLCODE_H__ + + +/* primary opcodes */ + +#define AML_NULL_CHAR (u16) 0x00 + +#define AML_ZERO_OP (u16) 0x00 +#define AML_ONE_OP (u16) 0x01 +#define AML_UNASSIGNED (u16) 0x02 +#define AML_ALIAS_OP (u16) 0x06 +#define AML_NAME_OP (u16) 0x08 +#define AML_BYTE_OP (u16) 0x0a +#define AML_WORD_OP (u16) 0x0b +#define AML_DWORD_OP (u16) 0x0c +#define AML_STRING_OP (u16) 0x0d +#define AML_SCOPE_OP (u16) 0x10 +#define AML_BUFFER_OP (u16) 0x11 +#define AML_PACKAGE_OP (u16) 0x12 +#define AML_METHOD_OP (u16) 0x14 +#define AML_DUAL_NAME_PREFIX (u16) 0x2e +#define AML_MULTI_NAME_PREFIX_OP (u16) 0x2f +#define AML_NAME_CHAR_SUBSEQ (u16) 0x30 +#define AML_NAME_CHAR_FIRST (u16) 0x41 +#define AML_OP_PREFIX (u16) 0x5b +#define AML_ROOT_PREFIX (u16) 0x5c +#define AML_PARENT_PREFIX (u16) 0x5e +#define AML_LOCAL_OP (u16) 0x60 +#define AML_LOCAL0 (u16) 0x60 +#define AML_LOCAL1 (u16) 0x61 +#define AML_LOCAL2 (u16) 0x62 +#define AML_LOCAL3 (u16) 0x63 +#define AML_LOCAL4 (u16) 0x64 +#define AML_LOCAL5 (u16) 0x65 +#define AML_LOCAL6 (u16) 0x66 +#define AML_LOCAL7 (u16) 0x67 +#define AML_ARG_OP (u16) 0x68 +#define AML_ARG0 (u16) 0x68 +#define AML_ARG1 (u16) 0x69 +#define AML_ARG2 (u16) 0x6a +#define AML_ARG3 (u16) 0x6b +#define AML_ARG4 (u16) 0x6c +#define AML_ARG5 (u16) 0x6d +#define AML_ARG6 (u16) 0x6e +#define AML_STORE_OP (u16) 0x70 +#define AML_REF_OF_OP (u16) 0x71 +#define AML_ADD_OP (u16) 0x72 +#define AML_CONCAT_OP (u16) 0x73 +#define AML_SUBTRACT_OP (u16) 0x74 +#define AML_INCREMENT_OP (u16) 0x75 +#define AML_DECREMENT_OP (u16) 0x76 +#define AML_MULTIPLY_OP (u16) 0x77 +#define AML_DIVIDE_OP (u16) 0x78 +#define AML_SHIFT_LEFT_OP (u16) 0x79 +#define AML_SHIFT_RIGHT_OP (u16) 0x7a +#define AML_BIT_AND_OP (u16) 0x7b +#define AML_BIT_NAND_OP (u16) 0x7c +#define AML_BIT_OR_OP (u16) 0x7d +#define AML_BIT_NOR_OP (u16) 0x7e +#define AML_BIT_XOR_OP (u16) 0x7f +#define AML_BIT_NOT_OP (u16) 0x80 +#define AML_FIND_SET_LEFT_BIT_OP (u16) 0x81 +#define AML_FIND_SET_RIGHT_BIT_OP (u16) 0x82 +#define AML_DEREF_OF_OP (u16) 0x83 +#define AML_NOTIFY_OP (u16) 0x86 +#define AML_SIZE_OF_OP (u16) 0x87 +#define AML_INDEX_OP (u16) 0x88 +#define AML_MATCH_OP (u16) 0x89 +#define AML_DWORD_FIELD_OP (u16) 0x8a +#define AML_WORD_FIELD_OP (u16) 0x8b +#define AML_BYTE_FIELD_OP (u16) 0x8c +#define AML_BIT_FIELD_OP (u16) 0x8d +#define AML_TYPE_OP (u16) 0x8e +#define AML_LAND_OP (u16) 0x90 +#define AML_LOR_OP (u16) 0x91 +#define AML_LNOT_OP (u16) 0x92 +#define AML_LEQUAL_OP (u16) 0x93 +#define AML_LGREATER_OP (u16) 0x94 +#define AML_LLESS_OP (u16) 0x95 +#define AML_IF_OP (u16) 0xa0 +#define AML_ELSE_OP (u16) 0xa1 +#define AML_WHILE_OP (u16) 0xa2 +#define AML_NOOP_CODE (u16) 0xa3 +#define AML_RETURN_OP (u16) 0xa4 +#define AML_BREAK_OP (u16) 0xa5 +#define AML_BREAK_POINT_OP (u16) 0xcc +#define AML_ONES_OP (u16) 0xff + +/* prefixed opcodes */ + +#define AML_EXTOP (u16) 0x005b + + +#define AML_MUTEX_OP (u16) 0x5b01 +#define AML_EVENT_OP (u16) 0x5b02 +#define AML_SHIFT_RIGHT_BIT_OP (u16) 0x5b10 +#define AML_SHIFT_LEFT_BIT_OP (u16) 0x5b11 +#define AML_COND_REF_OF_OP (u16) 0x5b12 +#define AML_CREATE_FIELD_OP (u16) 0x5b13 +#define AML_LOAD_OP (u16) 0x5b20 +#define AML_STALL_OP (u16) 0x5b21 +#define AML_SLEEP_OP (u16) 0x5b22 +#define AML_ACQUIRE_OP (u16) 0x5b23 +#define AML_SIGNAL_OP (u16) 0x5b24 +#define AML_WAIT_OP (u16) 0x5b25 +#define AML_RESET_OP (u16) 0x5b26 +#define AML_RELEASE_OP (u16) 0x5b27 +#define AML_FROM_BCDOP (u16) 0x5b28 +#define AML_TO_BCDOP (u16) 0x5b29 +#define AML_UN_LOAD_OP (u16) 0x5b2a +#define AML_REVISION_OP (u16) 0x5b30 +#define AML_DEBUG_OP (u16) 0x5b31 +#define AML_FATAL_OP (u16) 0x5b32 +#define AML_REGION_OP (u16) 0x5b80 +#define AML_DEF_FIELD_OP (u16) 0x5b81 +#define AML_DEVICE_OP (u16) 0x5b82 +#define AML_PROCESSOR_OP (u16) 0x5b83 +#define AML_POWER_RES_OP (u16) 0x5b84 +#define AML_THERMAL_ZONE_OP (u16) 0x5b85 +#define AML_INDEX_FIELD_OP (u16) 0x5b86 +#define AML_BANK_FIELD_OP (u16) 0x5b87 + + +/* Bogus opcodes (they are actually two separate opcodes) */ + +#define AML_LGREATEREQUAL_OP (u16) 0x9295 +#define AML_LLESSEQUAL_OP (u16) 0x9294 +#define AML_LNOTEQUAL_OP (u16) 0x9293 + + +/* Internal opcodes */ + +#define AML_NAMEPATH_OP (u16) 0x002d +#define AML_NAMEDFIELD_OP (u16) 0x0030 +#define AML_RESERVEDFIELD_OP (u16) 0x0031 +#define AML_ACCESSFIELD_OP (u16) 0x0032 +#define AML_BYTELIST_OP (u16) 0x0033 +#define AML_STATICSTRING_OP (u16) 0x0034 +#define AML_METHODCALL_OP (u16) 0x0035 + + +/* + * argument types + */ + +/* +#define AML_ASCIICHARLIST_ARG 'A' +#define AML_BYTEDATA_ARG 'b' +#define AML_BYTELIST_ARG 'B' +#define AML_DWORDDATA_ARG 'd' +#define AML_DATAOBJECT_ARG 'o' +#define AML_DATAOBJECTLIST_ARG 'O' +#define AML_FIELDLIST_ARG 'F' +#define AML_NAMESTRING_ARG 'n' +#define AML_OBJECTLIST_ARG 'P' +#define AML_PKGLENGTH_ARG 'p' +#define AML_SUPERNAME_ARG 's' +#define AML_TARGET_ARG 'l' +#define AML_TERMARG_ARG 't' +#define AML_TERMLIST_ARG 'T' +#define AML_WORDDATA_ARG 'w' +*/ + + +#define ARG_NONE 0x0 + +/* + * Argument types for the AML Parser + * Each field in the Arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types + */ + +#define ARGP_BYTEDATA 0x01 +#define ARGP_BYTELIST 0x02 +#define ARGP_CHARLIST 0x03 +#define ARGP_DATAOBJ 0x04 +#define ARGP_DATAOBJLIST 0x05 +#define ARGP_DWORDDATA 0x06 +#define ARGP_FIELDLIST 0x07 +#define ARGP_NAME 0x08 +#define ARGP_NAMESTRING 0x09 +#define ARGP_OBJLIST 0x0A +#define ARGP_PKGLENGTH 0x0B +#define ARGP_SUPERNAME 0x0C +#define ARGP_TARGET 0x0D +#define ARGP_TERMARG 0x0E +#define ARGP_TERMLIST 0x0F +#define ARGP_WORDDATA 0x10 + +/* + * Resolved argument types for the AML Interpreter + * Each field in the Arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types + */ + +#define ARGI_ANYTYPE 0x01 +#define ARGI_TARGETREF 0x02 +#define ARGI_REFERENCE 0x03 +#define ARGI_IF 0x04 +#define ARGI_NUMBER 0x05 +#define ARGI_STRING 0x06 +#define ARGI_BUFFER 0x07 +#define ARGI_PACKAGE 0x08 +#define ARGI_DATAOBJECT 0x09 /* Buffer, string, package or NTE reference - Used only by Size_of operator*/ +#define ARGI_COMPLEXOBJ 0x0A /* Buffer or package */ +#define ARGI_MUTEX 0x0B +#define ARGI_EVENT 0x0C +#define ARGI_REGION 0x0D +#define ARGI_DDBHANDLE 0x0E + +#define ARGI_INVALID_OPCODE 0xFFFFFFFF + + +/* + * hash offsets + */ +#define AML_EXTOP_HASH_OFFSET 22 +#define AML_LNOT_HASH_OFFSET 19 + + +/* + * opcode groups and types + */ + +#define OPGRP_NAMED 0x01 +#define OPGRP_FIELD 0x02 +#define OPGRP_BYTELIST 0x04 + +#define OPTYPE_UNDEFINED 0 + + +#define OPTYPE_LITERAL 1 +#define OPTYPE_CONSTANT 2 +#define OPTYPE_METHOD_ARGUMENT 3 +#define OPTYPE_LOCAL_VARIABLE 4 +#define OPTYPE_DATA_TERM 5 + +/* Type 1 opcodes */ + +#define OPTYPE_MONADIC1 6 +#define OPTYPE_DYADIC1 7 + + +/* Type 2 opcodes */ + +#define OPTYPE_MONADIC2 8 +#define OPTYPE_MONADIC2_r 9 +#define OPTYPE_DYADIC2 10 +#define OPTYPE_DYADIC2_r 11 +#define OPTYPE_DYADIC2_s 12 +#define OPTYPE_INDEX 13 +#define OPTYPE_MATCH 14 + +/* Generic for an op that returns a value */ + +#define OPTYPE_METHOD_CALL 15 + + +/* Misc */ + +#define OPTYPE_CREATE_FIELD 16 +#define OPTYPE_FATAL 17 +#define OPTYPE_CONTROL 18 +#define OPTYPE_RECONFIGURATION 19 +#define OPTYPE_NAMED_OBJECT 20 + +#define OPTYPE_BOGUS 21 + + +/* Comparison operation codes for Match_op operator */ + +typedef enum +{ + MATCH_MTR = 0, + MATCH_MEQ = 1, + MATCH_MLE = 2, + MATCH_MLT = 3, + MATCH_MGE = 4, + MATCH_MGT = 5 + +} AML_MATCH_OPERATOR; + +#define MAX_MATCH_OPERATOR 5 + + +/* Field Access Types */ + +#define ACCESS_TYPE_MASK 0x0f +#define ACCESS_TYPE_SHIFT 0 + +typedef enum +{ + ACCESS_ANY_ACC = 0, + ACCESS_BYTE_ACC = 1, + ACCESS_WORD_ACC = 2, + ACCESS_DWORD_ACC = 3, + ACCESS_BLOCK_ACC = 4, + ACCESS_SMBSEND_RECV_ACC = 5, + ACCESS_SMBQUICK_ACC = 6 + +} AML_ACCESS_TYPE; + + +/* Field Lock Rules */ + +#define LOCK_RULE_MASK 0x10 +#define LOCK_RULE_SHIFT 4 + +typedef enum +{ + GLOCK_NEVER_LOCK = 0, + GLOCK_ALWAYS_LOCK = 1 + +} AML_LOCK_RULE; + + +/* Field Update Rules */ + +#define UPDATE_RULE_MASK 0x060 +#define UPDATE_RULE_SHIFT 5 + +typedef enum +{ + UPDATE_PRESERVE = 0, + UPDATE_WRITE_AS_ONES = 1, + UPDATE_WRITE_AS_ZEROS = 2 + +} AML_UPDATE_RULE; + + +/* bit fields in Method_flags byte */ + +#define METHOD_FLAGS_ARG_COUNT 0x07 +#define METHOD_FLAGS_SERIALIZED 0x08 + + +/* Array sizes. Used for range checking also */ + +#define NUM_REGION_TYPES 5 +#define NUM_ACCESS_TYPES 7 +#define NUM_UPDATE_RULES 3 +#define NUM_MATCH_OPS 7 +#define NUM_OPCODES 256 +#define NUM_FIELD_NAMES 2 + +/* External declarations of the AML tables */ + +extern u8 acpi_gbl_aml [NUM_OPCODES]; +extern u16 acpi_gbl_pfx [NUM_OPCODES]; +extern char *acpi_gbl_short_ops [NUM_OPCODES]; +extern char *acpi_gbl_long_ops [NUM_OPCODES]; +extern char *acpi_gbl_region_types [NUM_REGION_TYPES]; +extern char *acpi_gbl_match_ops [NUM_MATCH_OPS]; +extern char *acpi_gbl_access_types [NUM_ACCESS_TYPES]; +extern char *acpi_gbl_update_rules [NUM_UPDATE_RULES]; +extern char *acpi_gbl_FEnames [NUM_FIELD_NAMES]; + + +/* + * AML tables + */ + +#ifdef DEFINE_AML_GLOBALS + +/* Data used in keeping track of fields */ + +char *acpi_gbl_FEnames[NUM_FIELD_NAMES] = +{ + "skip", + "?access?" +}; /* FE = Field Element */ + + +/* Region type decoding */ + +char *acpi_gbl_region_types[NUM_REGION_TYPES] = +{ + "System_memory", + "System_iO", + "PCIConfig", + "Embedded_control", + "SMBus" +}; + + +char *acpi_gbl_match_ops[NUM_MATCH_OPS] = +{ + "Error", + "MTR", + "MEQ", + "MLE", + "MLT", + "MGE", + "MGT" +}; + + +/* Access type decoding */ + +char *acpi_gbl_access_types[NUM_ACCESS_TYPES] = +{ + "Any_acc", + "Byte_acc", + "Word_acc", + "DWord_acc", + "Block_acc", + "SMBSend_recv_acc", + "SMBQuick_acc" +}; + + +/* Update rule decoding */ + +char *acpi_gbl_update_rules[NUM_UPDATE_RULES] = +{ + "Preserve", + "Write_as_ones", + "Write_as_zeros" +}; + + +#endif /* DEFINE_AML_GLOBALS */ + +#endif /* __AMLCODE_H__ */ diff --git a/drivers/acpi/include/common.h b/drivers/acpi/include/common.h new file mode 100644 index 000000000..f35ba795d --- /dev/null +++ b/drivers/acpi/include/common.h @@ -0,0 +1,650 @@ + +/****************************************************************************** + * + * Name: common.h -- prototypes for the common (subsystem-wide) procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _COMMON_H +#define _COMMON_H + + +#define REF_INCREMENT (u16) 0 +#define REF_DECREMENT (u16) 1 +#define REF_FORCE_DELETE (u16) 2 + +/* Acpi_cm_dump_buffer */ + +#define DB_BYTE_DISPLAY 1 +#define DB_WORD_DISPLAY 2 +#define DB_DWORD_DISPLAY 4 +#define DB_QWORD_DISPLAY 8 + + +/* Global initialization interfaces */ + +void +acpi_cm_init_globals ( + ACPI_INIT_DATA *init_data); + +void +acpi_cm_terminate ( + void); + + +/* + * Acpi_cm_init - miscellaneous initialization and shutdown + */ + +ACPI_STATUS +acpi_cm_hardware_initialize ( + void); + +ACPI_STATUS +acpi_cm_subsystem_shutdown ( + void); + +/* + * Acpi_cm_global - Global data structures and procedures + */ + +char * +acpi_cm_get_mutex_name ( + u32 mutex_id); + +char * +acpi_cm_get_type_name ( + u32 type); + +u8 +acpi_cm_valid_object_type ( + u32 type); + +ACPI_OWNER_ID +acpi_cm_allocate_owner_id ( + u32 id_type); + + +/* + * Acpi_cm_clib - Local implementations of C library functions + */ + +ACPI_SIZE +acpi_cm_strlen ( + const char *string); + +char * +acpi_cm_strcpy ( + char *dst_string, + const char *src_string); + +char * +acpi_cm_strncpy ( + char *dst_string, + const char *src_string, + ACPI_SIZE count); + +u32 +acpi_cm_strncmp ( + const char *string1, + const char *string2, + ACPI_SIZE count); + +u32 +acpi_cm_strcmp ( + const char *string1, + const char *string2); + +char * +acpi_cm_strcat ( + char *dst_string, + const char *src_string); + +char * +acpi_cm_strncat ( + char *dst_string, + const char *src_string, + ACPI_SIZE count); + +u32 +acpi_cm_strtoul ( + const char *string, + char **terminator, + s32 base); + +char * +acpi_cm_strstr ( + char *string1, + char *string2); + +char * +acpi_cm_strupr ( + char *src_string); + +void * +acpi_cm_memcpy ( + void *dest, + const void *src, + ACPI_SIZE count); + +void * +acpi_cm_memset ( + void *dest, + s32 value, + ACPI_SIZE count); + +s32 +acpi_cm_to_upper ( + s32 c); + +s32 +acpi_cm_to_lower ( + s32 c); + + +/* + * Acpi_cm_copy - Object construction and conversion interfaces + */ + +ACPI_STATUS +acpi_cm_build_simple_object( + ACPI_OBJECT_INTERNAL *obj, + ACPI_OBJECT *user_obj, + char *data_space, + u32 *buffer_space_used); + +ACPI_STATUS +acpi_cm_build_package_object ( + ACPI_OBJECT_INTERNAL *obj, + char *buffer, + u32 *space_used); + +ACPI_STATUS +acpi_cm_build_external_object ( + ACPI_OBJECT_INTERNAL *obj, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_cm_build_internal_simple_object( + ACPI_OBJECT *user_obj, + ACPI_OBJECT_INTERNAL *obj); + +ACPI_STATUS +acpi_cm_build_internal_object ( + ACPI_OBJECT *obj, + ACPI_OBJECT_INTERNAL *internal_obj); + +ACPI_STATUS +acpi_cm_copy_internal_simple_object ( + ACPI_OBJECT_INTERNAL *source_obj, + ACPI_OBJECT_INTERNAL *dest_obj); + +ACPI_STATUS +acpi_cm_build_copy_internal_package_object ( + ACPI_OBJECT_INTERNAL *source_obj, + ACPI_OBJECT_INTERNAL *dest_obj); + + +/* + * Acpi_cm_create - Object creation + */ + +ACPI_STATUS +acpi_cm_update_object_reference ( + ACPI_OBJECT_INTERNAL *object, + u16 action); + +ACPI_OBJECT_INTERNAL * +_cm_create_internal_object ( + char *module_name, + s32 line_number, + s32 component_id, + OBJECT_TYPE_INTERNAL type); + + +/* + * Acpi_cm_debug - Debug interfaces + */ + +s32 +get_debug_level ( + void); + +void +set_debug_level ( + s32 level); + +void +function_trace ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name); + +void +function_trace_ptr ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + void *pointer); + +void +function_trace_u32 ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + u32 integer); + +void +function_trace_str ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + char *string); + +void +function_exit ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name); + +void +function_status_exit ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + ACPI_STATUS status); + +void +function_value_exit ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + NATIVE_UINT value); + +void +function_ptr_exit ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING function_name, + char *ptr); + +void +debug_print_prefix ( + ACPI_STRING module_name, + s32 line_number); + +void +debug_print ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + s32 print_level, + char *format, ...); + +void +debug_print_raw ( + char *format, ...); + +void +_report_info ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING message); + +void +_report_error ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING message); + +void +_report_warning ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING message); + +void +_report_success ( + ACPI_STRING module_name, + s32 line_number, + s32 component_id, + ACPI_STRING message); + +void +acpi_cm_dump_buffer ( + char *buffer, + u32 count, + u32 display, + s32 component_id); + + +/* + * Acpi_cm_delete - Object deletion + */ + +void +acpi_cm_delete_internal_obj ( + ACPI_OBJECT_INTERNAL *object); + +void +acpi_cm_delete_internal_package_object ( + ACPI_OBJECT_INTERNAL *object); + +void +acpi_cm_delete_internal_simple_object ( + ACPI_OBJECT_INTERNAL *object); + +ACPI_STATUS +acpi_cm_delete_internal_object_list ( + ACPI_OBJECT_INTERNAL **obj_list); + + +/* + * Acpi_cm_eval - object evaluation + */ + +/* Method name strings */ + +#define METHOD_NAME__HID "_HID" +#define METHOD_NAME__UID "_UID" +#define METHOD_NAME__ADR "_ADR" +#define METHOD_NAME__STA "_STA" +#define METHOD_NAME__REG "_REG" +#define METHOD_NAME__SEG "_SEG" +#define METHOD_NAME__BBN "_BBN" + + +ACPI_STATUS +acpi_cm_evaluate_numeric_object ( + char *method_name, + ACPI_NAMED_OBJECT *acpi_device, + u32 *address); + +ACPI_STATUS +acpi_cm_execute_HID ( + ACPI_NAMED_OBJECT *acpi_device, + DEVICE_ID *hid); + +ACPI_STATUS +acpi_cm_execute_STA ( + ACPI_NAMED_OBJECT *acpi_device, + u32 *status_flags); + +ACPI_STATUS +acpi_cm_execute_UID ( + ACPI_NAMED_OBJECT *acpi_device, + DEVICE_ID *uid); + + +/* + * Acpi_cm_error - exception interfaces + */ + +char * +acpi_cm_format_exception ( + ACPI_STATUS status); + + +/* + * Acpi_cm_mutex - mutual exclusion interfaces + */ + +ACPI_STATUS +acpi_cm_mutex_initialize ( + void); + +void +acpi_cm_mutex_terminate ( + void); + +ACPI_STATUS +acpi_cm_create_mutex ( + ACPI_MUTEX_HANDLE mutex_id); + +ACPI_STATUS +acpi_cm_delete_mutex ( + ACPI_MUTEX_HANDLE mutex_id); + +ACPI_STATUS +acpi_cm_acquire_mutex ( + ACPI_MUTEX_HANDLE mutex_id); + +ACPI_STATUS +acpi_cm_release_mutex ( + ACPI_MUTEX_HANDLE mutex_id); + + +/* + * Acpi_cm_object - internal object create/delete/cache routines + */ + +#define acpi_cm_create_internal_object(t) _cm_create_internal_object(_THIS_MODULE,__LINE__,_COMPONENT,t) +#define acpi_cm_allocate_object_desc() _cm_allocate_object_desc(_THIS_MODULE,__LINE__,_COMPONENT) + +void * +_cm_allocate_object_desc ( + char *module_name, + s32 line_number, + s32 component_id); + +void +acpi_cm_delete_object_desc ( + ACPI_OBJECT_INTERNAL *object); + +u8 +acpi_cm_valid_internal_object ( + void *object); + + +/* + * Acpi_cm_ref_cnt - Object reference count management + */ + +void +acpi_cm_add_reference ( + ACPI_OBJECT_INTERNAL *object); + +void +acpi_cm_remove_reference ( + ACPI_OBJECT_INTERNAL *object); + +/* + * Acpi_cm_size - Object size routines + */ + +ACPI_STATUS +acpi_cm_get_simple_object_size ( + ACPI_OBJECT_INTERNAL *obj, + u32 *obj_length); + +ACPI_STATUS +acpi_cm_get_package_object_size ( + ACPI_OBJECT_INTERNAL *obj, + u32 *obj_length); + +ACPI_STATUS +acpi_cm_get_object_size( + ACPI_OBJECT_INTERNAL *obj, + u32 *obj_length); + + +/* + * Acpi_cm_state - Generic state creation/cache routines + */ + +void +acpi_cm_push_generic_state ( + ACPI_GENERIC_STATE **list_head, + ACPI_GENERIC_STATE *state); + +ACPI_GENERIC_STATE * +acpi_cm_pop_generic_state ( + ACPI_GENERIC_STATE **list_head); + + +ACPI_GENERIC_STATE * +acpi_cm_create_generic_state ( + void); + +ACPI_GENERIC_STATE * +acpi_cm_create_update_state ( + ACPI_OBJECT_INTERNAL *object, + u16 action); + +ACPI_STATUS +acpi_cm_create_update_state_and_push ( + ACPI_OBJECT_INTERNAL *object, + u16 action, + ACPI_GENERIC_STATE **state_list); + +ACPI_GENERIC_STATE * +acpi_cm_create_control_state ( + void); + +void +acpi_cm_delete_generic_state ( + ACPI_GENERIC_STATE *state); + +void +acpi_cm_delete_generic_state_cache ( + void); + +void +acpi_cm_delete_object_cache ( + void); + +/* + * Acpi_cmutils + */ + +u8 +acpi_cm_valid_acpi_name ( + u32 name); + +u8 +acpi_cm_valid_acpi_character ( + char character); + + +/* + * Memory allocation functions and related macros. + * Macros that expand to include filename and line number + */ + +void * +_cm_allocate ( + u32 size, + u32 component, + ACPI_STRING module, + s32 line); + +void * +_cm_callocate ( + u32 size, + u32 component, + ACPI_STRING module, + s32 line); + +void +_cm_free ( + void *address, + u32 component, + ACPI_STRING module, + s32 line); + +void +acpi_cm_init_static_object ( + ACPI_OBJECT_INTERNAL *obj_desc); + +#define acpi_cm_allocate(a) _cm_allocate(a,_COMPONENT,_THIS_MODULE,__LINE__) +#define acpi_cm_callocate(a) _cm_callocate(a, _COMPONENT,_THIS_MODULE,__LINE__) +#define acpi_cm_free(a) _cm_free(a,_COMPONENT,_THIS_MODULE,__LINE__) + +#ifndef ACPI_DEBUG + +#define acpi_cm_add_element_to_alloc_list(a,b,c,d,e,f) +#define acpi_cm_delete_element_from_alloc_list(a,b,c,d) +#define acpi_cm_dump_current_allocations(a,b) +#define acpi_cm_dump_allocation_info() + +#define DECREMENT_OBJECT_METRICS(a) +#define INCREMENT_OBJECT_METRICS(a) +#define INITIALIZE_ALLOCATION_METRICS() + +#else + +#define INITIALIZE_ALLOCATION_METRICS() \ + acpi_gbl_current_object_count = 0; \ + acpi_gbl_current_object_size = 0; \ + acpi_gbl_running_object_count = 0; \ + acpi_gbl_running_object_size = 0; \ + acpi_gbl_max_concurrent_object_count = 0; \ + acpi_gbl_max_concurrent_object_size = 0; \ + acpi_gbl_current_alloc_size = 0; \ + acpi_gbl_current_alloc_count = 0; \ + acpi_gbl_running_alloc_size = 0; \ + acpi_gbl_running_alloc_count = 0; \ + acpi_gbl_max_concurrent_alloc_size = 0; \ + acpi_gbl_max_concurrent_alloc_count = 0 + +#define DECREMENT_OBJECT_METRICS(a) \ + acpi_gbl_current_object_count--; \ + acpi_gbl_current_object_size -= a + +#define INCREMENT_OBJECT_METRICS(a) \ + acpi_gbl_current_object_count++; \ + acpi_gbl_running_object_count++; \ + if (acpi_gbl_max_concurrent_object_count < acpi_gbl_current_object_count) \ + { \ + acpi_gbl_max_concurrent_object_count = acpi_gbl_current_object_count; \ + } \ + acpi_gbl_running_object_size += a; \ + acpi_gbl_current_object_size += a; \ + if (acpi_gbl_max_concurrent_object_size < acpi_gbl_current_object_size) \ + { \ + acpi_gbl_max_concurrent_object_size = acpi_gbl_current_object_size; \ + } + + +void +acpi_cm_dump_allocation_info ( + void); + +void +acpi_cm_dump_current_allocations ( + u32 component, + ACPI_STRING module); + +#endif + + +#endif /* _COMMON_H */ diff --git a/drivers/acpi/include/config.h b/drivers/acpi/include/config.h new file mode 100644 index 000000000..a0e77e113 --- /dev/null +++ b/drivers/acpi/include/config.h @@ -0,0 +1,185 @@ + +/****************************************************************************** + * + * Name: config.h - Global configuration constants + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + + +/****************************************************************************** + * + * Compile-time options + * + *****************************************************************************/ + +/* + * ACPI_DEBUG - This switch enables all the debug facilities of the ACPI + * subsystem. This includes the DEBUG_PRINT output statements + * When disabled, all DEBUG_PRINT statements are compiled out. + * + * ACPI_APPLICATION - Use this switch if the subsystem is going to be run + * at the application level. + * + */ + + +/****************************************************************************** + * + * Subsystem Constants + * + *****************************************************************************/ + + +/* Version string */ + +#define ACPI_CA_VERSION __DATE__ + +/* Name of host operating system (returned by the _OS_ namespace object) */ + +#ifdef _LINUX +#define ACPI_OS_NAME "Linux" +#else +#define ACPI_OS_NAME "Intel ACPI/CA Core Subsystem" +#endif + + +/* + * How and when control methods will be parsed + * The default action is to parse all methods at table load time to verify them, but delete the parse trees + * to conserve memory. Methods are parsed just in time before execution and the parse tree is deleted + * when execution completes. + */ +#define METHOD_PARSE_AT_INIT 0x0 /* Parse at table init, never delete the method parse tree */ +#define METHOD_PARSE_JUST_IN_TIME 0x1 /* Parse only when a method is invoked */ +#define METHOD_DELETE_AT_COMPLETION 0x2 /* Delete parse tree on method completion */ + +/* Default parsing configuration */ + +#define METHOD_PARSE_CONFIGURATION (METHOD_PARSE_JUST_IN_TIME | METHOD_DELETE_AT_COMPLETION) + + +/* Maximum objects in the various object caches */ + +#define MAX_STATE_CACHE_DEPTH 24 /* State objects for stacks */ +#define MAX_PARSE_CACHE_DEPTH 512 /* Parse tree objects */ +#define MAX_OBJECT_CACHE_DEPTH 32 /* Interpreter operand objects */ +#define MAX_WALK_CACHE_DEPTH 2 /* Objects for parse tree walks (method execution) */ + +/* + * Name_space Table size + * + * All tables are the same size to simplify the implementation. + * Tables may be extended by allocating additional tables that + * are in turn linked together to form a chain of tables. + */ + +#define NS_TABLE_SIZE 16 + +/* String size constants */ + +#define MAX_STRING_LENGTH 512 +#define PATHNAME_MAX 256 /* A full namespace pathname */ + + +/* Maximum count for a semaphore object */ + +#define MAX_SEMAPHORE_COUNT 256 + + +/* Max reference count (for debug only) */ + +#define MAX_REFERENCE_COUNT 0x200 + + +/* Size of cached memory mapping for system memory operation region */ + +#define SYSMEM_REGION_WINDOW_SIZE 4096 + + +/* + * Debugger threading model + * Use single threaded if the entire subsystem is contained in an application + * Use multiple threaded when the the subsystem is running in the kernel. + * + * By default the model is single threaded if ACPI_APPLICATION is set, + * multi-threaded if ACPI_APPLICATION is not set. + */ + +#define DEBUGGER_SINGLE_THREADED 0 +#define DEBUGGER_MULTI_THREADED 1 + +#ifdef ACPI_APPLICATION +#define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED + +#else +#define DEBUGGER_THREADING DEBUGGER_MULTI_THREADED +#endif + + +/****************************************************************************** + * + * ACPI Specification constants (Do not change unless the specification changes) + * + *****************************************************************************/ + +/* + * Method info (in WALK_STATE), containing local variables and argumetns + */ + +#define MTH_NUM_LOCALS 8 +#define MTH_MAX_LOCAL 7 + +#define MTH_NUM_ARGS 7 +#define MTH_MAX_ARG 6 + +/* + * Operand Stack (in WALK_STATE), Must be large enough to contain MTH_MAX_ARG + */ + +#define OBJ_NUM_OPERANDS 8 +#define OBJ_MAX_OPERAND 7 + +/* Names within the namespace are 4 bytes long */ + +#define ACPI_NAME_SIZE 4 +#define PATH_SEGMENT_LENGTH 5 /* 4 chars for name + 1 char for separator */ +#define PATH_SEPARATOR '.' + + +/* Constants used in searching for the RSDP in low memory */ + +#define LO_RSDP_WINDOW_BASE (void *) 0 +#define HI_RSDP_WINDOW_BASE (void *) 0xE0000 +#define LO_RSDP_WINDOW_SIZE 0x400 +#define HI_RSDP_WINDOW_SIZE 0x20000 +#define RSDP_SCAN_STEP 16 + + +/* Maximum nesting of package objects */ + +#define MAX_PACKAGE_DEPTH 16 + + +#endif /* _CONFIG_H */ + diff --git a/drivers/acpi/include/debugger.h b/drivers/acpi/include/debugger.h new file mode 100644 index 000000000..0d27d59b3 --- /dev/null +++ b/drivers/acpi/include/debugger.h @@ -0,0 +1,394 @@ + +/****************************************************************************** + * + * Name: debugger.h - ACPI/AML debugger + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __DEBUGGER_H__ +#define __DEBUGGER_H__ + + +#define DB_MAX_ARGS 8 /* Must be max method args + 1 */ + +#define DB_COMMAND_PROMPT '-' +#define DB_EXECUTE_PROMPT '%' + + +extern int optind; +extern char *optarg; +extern u8 *aml_ptr; +extern u32 acpi_aml_length; + +extern u8 opt_tables; +extern u8 opt_disasm; +extern u8 opt_stats; +extern u8 opt_parse_jit; +extern u8 opt_verbose; + + +extern char *args[DB_MAX_ARGS]; +extern char line_buf[80]; +extern char scope_buf[40]; +extern char debug_filename[40]; +extern u8 output_to_file; +extern char *buffer; +extern char *filename; +extern char *INDENT_STRING; +extern u32 acpi_gbl_method_breakpoint; +extern u8 acpi_gbl_db_output_flags; +extern u32 acpi_gbl_db_debug_level; +extern u32 acpi_gbl_db_console_debug_level; + +extern u32 num_names; +extern u32 num_methods; +extern u32 num_regions; +extern u32 num_packages; +extern u32 num_aliases; +extern u32 num_devices; +extern u32 num_field_defs; +extern u32 num_thermal_zones; +extern u32 num_named_objects; +extern u32 num_grammar_elements; +extern u32 num_method_elements ; +extern u32 num_mutexes; +extern u32 num_power_resources; +extern u32 num_bank_fields ; +extern u32 num_index_fields; +extern u32 num_events; + +extern u32 size_of_parse_tree; +extern u32 size_of_method_trees; +extern u32 size_of_nTes; +extern u32 size_of_acpi_objects; + + +#define BUFFER_SIZE 4196 + +#define DB_REDIRECTABLE_OUTPUT 0x01 +#define DB_CONSOLE_OUTPUT 0x02 +#define DB_DUPLICATE_OUTPUT 0x03 + + +typedef struct command_info +{ + char *name; /* Command Name */ + char min_args; /* Minimum arguments required */ + +} COMMAND_INFO; + + +typedef struct argument_info +{ + char *name; /* Argument Name */ + +} ARGUMENT_INFO; + + +#define PARAM_LIST(pl) pl + +#define DBTEST_OUTPUT_LEVEL(lvl) if (opt_verbose) + +#define VERBOSE_PRINT(fp) DBTEST_OUTPUT_LEVEL(lvl) {\ + acpi_os_printf PARAM_LIST(fp);} + +#define EX_NO_SINGLE_STEP 1 +#define EX_SINGLE_STEP 2 + + +/* Prototypes */ + + +/* + * dbapi - external debugger interfaces + */ + +int +acpi_db_initialize ( + void); + +ACPI_STATUS +acpi_db_single_step ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + u8 op_type); + + +/* + * dbcmds - debug commands and output routines + */ + + +void +acpi_db_display_table_info ( + char *table_arg); + +void +acpi_db_unload_acpi_table ( + char *table_arg, + char *instance_arg); + +void +acpi_db_set_method_breakpoint ( + char *location, + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +void +acpi_db_set_method_call_breakpoint ( + ACPI_GENERIC_OP *op); + +void +acpi_db_disassemble_aml ( + char *statements, + ACPI_GENERIC_OP *op); + +void +acpi_db_dump_namespace ( + char *start_arg, + char *depth_arg); + +void +acpi_db_dump_namespace_by_owner ( + char *owner_arg, + char *depth_arg); + +void +acpi_db_send_notify ( + char *name, + u32 value); + +void +acpi_db_set_method_data ( + char *type_arg, + char *index_arg, + char *value_arg); + +ACPI_STATUS +acpi_db_display_objects ( + char *obj_type_arg, + char *display_count_arg); + +ACPI_STATUS +acpi_db_find_name_in_namespace ( + char *name_arg); + +void +acpi_db_set_scope ( + char *name); + +void +acpi_db_find_references ( + char *object_arg); + + +/* + * dbdisasm - AML disassembler + */ + +void +acpi_db_display_op ( + ACPI_GENERIC_OP *origin, + u32 num_opcodes); + +void +acpi_db_display_namestring ( + char *name); + +void +acpi_db_display_path ( + ACPI_GENERIC_OP *op); + +void +acpi_db_display_opcode ( + ACPI_GENERIC_OP *op); + + +/* + * dbdisply - debug display commands + */ + + +void +acpi_db_display_method_info ( + ACPI_GENERIC_OP *op); + +void +acpi_db_decode_and_display_object ( + char *target, + char *output_type); + +void +acpi_db_display_result_object ( + ACPI_OBJECT_INTERNAL *ret_desc); + +ACPI_STATUS +acpi_db_display_all_methods ( + char *display_count_arg); + +void +acpi_db_display_internal_object ( + ACPI_OBJECT_INTERNAL *obj_desc); + +void +acpi_db_display_arguments ( + void); + +void +acpi_db_display_locals ( + void); + +void +acpi_db_display_results ( + void); + +void +acpi_db_display_calling_tree ( + void); + +void +acpi_db_display_argument_object ( + ACPI_OBJECT_INTERNAL *obj_desc); + + +/* + * dbexec - debugger control method execution + */ + +void +acpi_db_execute ( + char *name, + char **args, + u32 flags); + +void +acpi_db_create_execution_threads ( + char *num_threads_arg, + char *num_loops_arg, + char *method_name_arg); + + +/* + * dbfileio - Debugger file I/O commands + */ + +OBJECT_TYPE_INTERNAL +acpi_db_match_argument ( + char *user_argument, + ARGUMENT_INFO *arguments); + + +void +acpi_db_close_debug_file ( + void); + +void +acpi_db_open_debug_file ( + char *name); + +ACPI_STATUS +acpi_db_load_acpi_table ( + char *filename); + + +/* + * dbhistry - debugger HISTORY command + */ + +void +acpi_db_add_to_history ( + char *command_line); + +void +acpi_db_display_history (void); + +char * +acpi_db_get_from_history ( + char *command_num_arg); + + +/* + * dbinput - user front-end to the AML debugger + */ + +ACPI_STATUS +acpi_db_command_dispatch ( + char *input_buffer, + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +void +acpi_db_execute_thread ( + void *context); + +ACPI_STATUS +acpi_db_user_commands ( + char prompt, + ACPI_GENERIC_OP *op); + + +/* + * dbstats - Generation and display of ACPI table statistics + */ + +void +acpi_db_generate_statistics ( + ACPI_GENERIC_OP *root, + u8 is_method); + + +ACPI_STATUS +acpi_db_display_statistics ( + char *type_arg); + + +/* + * dbutils - AML debugger utilities + */ + +void +acpi_db_set_output_destination ( + s32 where); + +void +acpi_db_dump_buffer ( + u32 address); + +void +acpi_db_dump_object ( + ACPI_OBJECT *obj_desc, + u32 level); + +void +acpi_db_prep_namestring ( + char *name); + + +ACPI_STATUS +acpi_db_second_pass_parse ( + ACPI_GENERIC_OP *root); + +ACPI_NAMED_OBJECT* +acpi_db_local_ns_lookup ( + char *name); + + +#endif /* __DEBUGGER_H__ */ diff --git a/drivers/acpi/include/dispatch.h b/drivers/acpi/include/dispatch.h new file mode 100644 index 000000000..e1d44be7a --- /dev/null +++ b/drivers/acpi/include/dispatch.h @@ -0,0 +1,383 @@ +/****************************************************************************** + * + * Module Name: dispatch.h + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _DISPATCH_H_ +#define _DISPATCH_H_ + + +#define NAMEOF_LOCAL_NTE "__L0" +#define NAMEOF_ARG_NTE "__A0" + + +/* For Acpi_ds_method_data_set_value */ + +#define MTH_TYPE_LOCAL 0 +#define MTH_TYPE_ARG 1 + + +/* Common interfaces */ + +ACPI_STATUS +acpi_ds_obj_stack_push ( + void *object, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_obj_stack_pop ( + u32 pop_count, + ACPI_WALK_STATE *walk_state); + +void * +acpi_ds_obj_stack_get_value ( + u32 index, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_obj_stack_pop_object ( + ACPI_OBJECT_INTERNAL **object, + ACPI_WALK_STATE *walk_state); + + +/* dsregion - Op region support */ + +ACPI_STATUS +acpi_ds_get_region_arguments ( + ACPI_OBJECT_INTERNAL *rgn_desc); + + +/* dsctrl - Parser/Interpreter interface, control stack routines */ + +/* +ACPI_CTRL_STATE * +Acpi_ds_create_control_state (void); + +void +Acpi_ds_push_control_state ( + ACPI_CTRL_STATE *Control_state, + ACPI_WALK_STATE *Walk_state); + +ACPI_CTRL_STATE * +Acpi_ds_pop_control_state ( + ACPI_WALK_STATE *Walk_state); +*/ + +ACPI_STATUS +acpi_ds_exec_begin_control_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_exec_end_control_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + + +/* dsexec - Parser/Interpreter interface, method execution callbacks */ + +ACPI_STATUS +acpi_ds_exec_begin_op ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_exec_end_op ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op); + + +/* dsfield - Parser/Interpreter interface for AML fields */ + + +ACPI_STATUS +acpi_ds_create_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_create_bank_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_create_index_field ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE region, + ACPI_WALK_STATE *walk_state); + + +/* dsload - Parser/Interpreter interface, namespace load callbacks */ + +ACPI_STATUS +acpi_ds_load1_begin_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_load1_end_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_load2_begin_op ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_load2_end_op ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op); + + +/* dsmthdat - method data (locals/args) */ + + +ACPI_STATUS +acpi_ds_method_data_delete_all ( + ACPI_WALK_STATE *walk_state); + +u8 +acpi_ds_is_method_value ( + ACPI_OBJECT_INTERNAL *obj_desc); + +OBJECT_TYPE_INTERNAL +acpi_ds_method_data_get_type ( + u32 type, + u32 index); + +ACPI_STATUS +acpi_ds_method_data_get_value ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL **obj_desc); + +ACPI_STATUS +acpi_ds_method_data_set_value ( + u32 type, + u32 index, + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_ds_method_data_delete_value ( + u32 type, + u32 index); + +ACPI_STATUS +acpi_ds_method_data_init_args ( + ACPI_OBJECT_INTERNAL **params, + u32 param_count); + +ACPI_NAMED_OBJECT* +acpi_ds_method_data_get_nte ( + u32 type, + u32 index); + +ACPI_STATUS +acpi_ds_method_data_init ( + ACPI_WALK_STATE *walk_state); + + +/* dsmethod - Parser/Interpreter interface - control method parsing */ + +ACPI_STATUS +acpi_ds_parse_method ( + ACPI_HANDLE obj_handle); + +ACPI_STATUS +acpi_ds_call_control_method ( + ACPI_WALK_LIST *walk_list, + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_restart_control_method ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL *return_desc); + +ACPI_STATUS +acpi_ds_terminate_control_method ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_begin_method_execution ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL *obj_desc); + + +/* dsobj - Parser/Interpreter interface - object initialization and conversion */ + +ACPI_STATUS +acpi_ds_init_one_object ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value); + +ACPI_STATUS +acpi_ds_initialize_objects ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAMED_OBJECT *start_entry); + +ACPI_STATUS +acpi_ds_build_internal_package_obj ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL **obj_desc); + +ACPI_STATUS +acpi_ds_build_internal_object ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL **obj_desc_ptr); + +ACPI_STATUS +acpi_ds_init_object_from_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + u16 opcode, + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_ds_create_named_object ( + ACPI_WALK_STATE *walk_state, + ACPI_NAMED_OBJECT *entry, + ACPI_GENERIC_OP *op); + + +/* dsregn - Parser/Interpreter interface - Op Region parsing */ + +ACPI_STATUS +acpi_ds_eval_region_operands ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op); + +ACPI_STATUS +acpi_ds_initialize_region ( + ACPI_HANDLE obj_handle); + + +/* dsutils - Parser/Interpreter interface utility routines */ + +void +acpi_ds_delete_result_if_not_used ( + ACPI_GENERIC_OP *op, + ACPI_OBJECT_INTERNAL *result_obj, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_create_operand ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *arg); + +ACPI_STATUS +acpi_ds_create_operands ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *first_arg); + +ACPI_STATUS +acpi_ds_resolve_operands ( + ACPI_WALK_STATE *walk_state); + +OBJECT_TYPE_INTERNAL +acpi_ds_map_opcode_to_data_type ( + u16 opcode, + u32 *out_flags); + +OBJECT_TYPE_INTERNAL +acpi_ds_map_named_opcode_to_data_type ( + u16 opcode); + + +/* + * dswscope - Scope Stack manipulation + */ + +ACPI_STATUS +acpi_ds_scope_stack_push ( + ACPI_NAME_TABLE *new_scope, + OBJECT_TYPE_INTERNAL type, + ACPI_WALK_STATE *walk_state); + + +ACPI_STATUS +acpi_ds_scope_stack_pop ( + ACPI_WALK_STATE *walk_state); + +void +acpi_ds_scope_stack_clear ( + ACPI_WALK_STATE *walk_state); + + +/* Acpi_dswstate - parser WALK_STATE management routines */ + +ACPI_WALK_STATE * +acpi_ds_create_walk_state ( + ACPI_OWNER_ID owner_id, + ACPI_GENERIC_OP *origin, + ACPI_OBJECT_INTERNAL *mth_desc, + ACPI_WALK_LIST *walk_list); + +ACPI_STATUS +acpi_ds_obj_stack_delete_all ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_obj_stack_pop_and_delete ( + u32 pop_count, + ACPI_WALK_STATE *walk_state); + +void +acpi_ds_delete_walk_state ( + ACPI_WALK_STATE *walk_state); + +ACPI_WALK_STATE * +acpi_ds_pop_walk_state ( + ACPI_WALK_LIST *walk_list); + +ACPI_STATUS +acpi_ds_result_stack_pop ( + ACPI_OBJECT_INTERNAL **object, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_result_stack_push ( + void *object, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ds_result_stack_clear ( + ACPI_WALK_STATE *walk_state); + +ACPI_WALK_STATE * +acpi_ds_get_current_walk_state ( + ACPI_WALK_LIST *walk_list); + +void +acpi_ds_delete_walk_state_cache ( + void); + + +#endif /* _DISPATCH_H_ */
\ No newline at end of file diff --git a/drivers/acpi/include/events.h b/drivers/acpi/include/events.h new file mode 100644 index 000000000..398157004 --- /dev/null +++ b/drivers/acpi/include/events.h @@ -0,0 +1,209 @@ + +/****************************************************************************** + * + * Name: events.h - Acpi_event subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __EVENTS_H__ +#define __EVENTS_H__ + + +/* + * Acpi_evfixed - Fixed event handling + */ + +ACPI_STATUS +acpi_ev_fixed_event_initialize ( + void); + +u32 +acpi_ev_fixed_event_detect ( + void); + +u32 +acpi_ev_fixed_event_dispatch ( + u32 acpi_event); + + +/* + * Acpi_evglock - Global Lock support + */ + +ACPI_STATUS +acpi_ev_acquire_global_lock( + void); + +void +acpi_ev_release_global_lock( + void); + +ACPI_STATUS +acpi_ev_init_global_lock_handler ( + void); + + +/* + * Acpi_evgpe - GPE handling and dispatch + */ + +ACPI_STATUS +acpi_ev_gpe_initialize ( + void); + +ACPI_STATUS +acpi_ev_init_gpe_control_methods ( + void); + +u32 +acpi_ev_gpe_dispatch ( + u32 gpe_number); + +u32 +acpi_ev_gpe_detect ( + void); + + +/* + * Acpi_evnotify - Device Notify handling and dispatch + */ + +void +acpi_ev_notify_dispatch ( + ACPI_HANDLE device, + u32 notify_value); + + +/* + * Acpi_evregion - Address Space handling + */ + +ACPI_STATUS +acpi_ev_install_default_address_space_handlers ( + void); + +ACPI_STATUS +acpi_ev_address_space_dispatch ( + ACPI_OBJECT_INTERNAL *region_obj, + u32 function, + u32 address, + u32 bit_width, + u32 *value); + + +ACPI_STATUS +acpi_ev_addr_handler_helper ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value); + +void +acpi_ev_disassociate_region_from_handler( + ACPI_OBJECT_INTERNAL *region_obj); + + +ACPI_STATUS +acpi_ev_associate_region_and_handler( + ACPI_OBJECT_INTERNAL *handler_obj, + ACPI_OBJECT_INTERNAL *region_obj); + + +/* + * Acpi_evregini - Region initialization and setup + */ + +ACPI_STATUS +acpi_ev_system_memory_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context); + +ACPI_STATUS +acpi_ev_io_space_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context); + +ACPI_STATUS +acpi_ev_pci_config_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context); + +ACPI_STATUS +acpi_ev_default_region_setup ( + ACPI_HANDLE handle, + u32 function, + void *handler_context, + void **return_context); + +ACPI_STATUS +acpi_ev_initialize_region ( + ACPI_OBJECT_INTERNAL *region_obj, + u8 acpi_ns_locked); + + +/* + * Acpi_evsci - SCI (System Control Interrupt) handling/dispatch + */ + +u32 +acpi_ev_install_sci_handler ( + void); + +ACPI_STATUS +acpi_ev_remove_sci_handler ( + void); + +s32 +acpi_ev_initialize_sCI ( + s32 program_sCI); + +void +acpi_ev_restore_acpi_state ( + void); + +void +acpi_ev_terminate ( + void); + + +/* Debug support */ + +#ifdef ACPI_DEBUG + +s32 +acpi_ev_sci_count ( + u32 acpi_event); + +#define DEBUG_INCREMENT_EVENT_COUNT(a) acpi_gbl_event_count[a]++; + +#else + +#define DEBUG_INCREMENT_EVENT_COUNT(a) +#endif + + +#endif /* __EVENTS_H__ */ diff --git a/drivers/acpi/include/globals.h b/drivers/acpi/include/globals.h new file mode 100644 index 000000000..7857d6f39 --- /dev/null +++ b/drivers/acpi/include/globals.h @@ -0,0 +1,311 @@ + +/****************************************************************************** + * + * Name: globals.h - Declarations for global variables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __GLOBALS_H__ +#define __GLOBALS_H__ + + +/* + * Ensure that the globals are actually defined only once + */ +#ifdef DEFINE_ACPI_GLOBALS +#define ACPI_EXTERN +#else +#define ACPI_EXTERN extern +#endif + + +extern char *msg_acpi_error_break; + +/***************************************************************************** + * + * Debug support + * + ****************************************************************************/ + +/* Runtime configuration of debug print levels */ + +extern u32 acpi_dbg_level; +extern u32 acpi_dbg_layer; + + +/* Procedure nesting level for debug output */ + +extern u32 acpi_gbl_nesting_level; + + +/***************************************************************************** + * + * ACPI Table globals + * + ****************************************************************************/ + +/* + * Table pointers. + * Although these pointers are somewhat redundant with the global Acpi_table, + * they are convenient because they are typed pointers. + * + * These tables are single-table only; meaning that there can be at most one + * of each in the system. Each global points to the actual table. + * + */ +ACPI_EXTERN ROOT_SYSTEM_DESCRIPTOR_POINTER *acpi_gbl_RSDP; +ACPI_EXTERN ROOT_SYSTEM_DESCRIPTION_TABLE *acpi_gbl_RSDT; +ACPI_EXTERN FIRMWARE_ACPI_CONTROL_STRUCTURE *acpi_gbl_FACS; +ACPI_EXTERN FIXED_ACPI_DESCRIPTION_TABLE *acpi_gbl_FACP; +ACPI_EXTERN APIC_TABLE *acpi_gbl_APIC; +ACPI_EXTERN ACPI_TABLE_HEADER *acpi_gbl_DSDT; +ACPI_EXTERN ACPI_TABLE_HEADER *acpi_gbl_SBST; +/* + * Since there may be multiple SSDTs and PSDTS, a single pointer is not + * sufficient; Therefore, there isn't one! + */ + + +/* + * ACPI Table info arrays + */ +extern ACPI_TABLE_DESC acpi_gbl_acpi_tables[NUM_ACPI_TABLES]; +extern ACPI_TABLE_SUPPORT acpi_gbl_acpi_table_data[NUM_ACPI_TABLES]; + +/* + * Predefined mutex objects. This array contains the + * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs. + * (The table maps local handles to the real OS handles) + */ +ACPI_EXTERN ACPI_MUTEX_INFO acpi_gbl_acpi_mutex_info [NUM_MTX]; +extern ACPI_INIT_DATA acpi_gbl_acpi_init_data; + + +/***************************************************************************** + * + * Miscellaneous globals + * + ****************************************************************************/ + + +ACPI_EXTERN u8 *acpi_gbl_gpe0enable_register_save; +ACPI_EXTERN u8 *acpi_gbl_gpe1_enable_register_save; +ACPI_EXTERN ACPI_WALK_STATE *acpi_gbl_breakpoint_walk; +ACPI_EXTERN ACPI_GENERIC_STATE *acpi_gbl_generic_state_cache; +ACPI_EXTERN ACPI_GENERIC_OP *acpi_gbl_parse_cache; +ACPI_EXTERN ACPI_OBJECT_INTERNAL *acpi_gbl_object_cache; +ACPI_EXTERN ACPI_WALK_STATE *acpi_gbl_walk_state_cache; +ACPI_EXTERN ACPI_HANDLE acpi_gbl_global_lock_semaphore; + + +ACPI_EXTERN u32 acpi_gbl_global_lock_thread_count; +ACPI_EXTERN u32 acpi_gbl_restore_acpi_chipset; +ACPI_EXTERN u32 acpi_gbl_original_mode; +ACPI_EXTERN u32 acpi_gbl_edge_level_save; +ACPI_EXTERN u32 acpi_gbl_irq_enable_save; +ACPI_EXTERN u32 acpi_gbl_rsdp_original_location; + +ACPI_EXTERN u32 acpi_gbl_state_cache_requests; +ACPI_EXTERN u32 acpi_gbl_state_cache_hits; +ACPI_EXTERN u32 acpi_gbl_parse_cache_requests; +ACPI_EXTERN u32 acpi_gbl_parse_cache_hits; +ACPI_EXTERN u32 acpi_gbl_object_cache_requests; +ACPI_EXTERN u32 acpi_gbl_object_cache_hits; +ACPI_EXTERN u32 acpi_gbl_walk_state_cache_requests; +ACPI_EXTERN u32 acpi_gbl_walk_state_cache_hits; +ACPI_EXTERN u32 acpi_gbl_ns_lookup_count; +ACPI_EXTERN u32 acpi_gbl_ps_find_count; + + +ACPI_EXTERN u16 acpi_gbl_generic_state_cache_depth; +ACPI_EXTERN u16 acpi_gbl_parse_cache_depth; +ACPI_EXTERN u16 acpi_gbl_object_cache_depth; +ACPI_EXTERN u16 acpi_gbl_walk_state_cache_depth; +ACPI_EXTERN u16 acpi_gbl_pm1_enable_register_save; +ACPI_EXTERN u16 acpi_gbl_next_table_owner_id; +ACPI_EXTERN u16 acpi_gbl_next_method_owner_id; + +ACPI_EXTERN u8 acpi_gbl_debugger_configuration; +ACPI_EXTERN u8 acpi_gbl_global_lock_acquired; +ACPI_EXTERN u8 acpi_gbl_global_lock_set; /* TBD: [Restructure] OBSOLETE?? */ +ACPI_EXTERN u8 acpi_gbl_step_to_next_call; +ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; + + +ACPI_EXTERN ACPI_OBJECT_NOTIFY_HANDLER acpi_gbl_drv_notify; +ACPI_EXTERN ACPI_OBJECT_NOTIFY_HANDLER acpi_gbl_sys_notify; + + +extern u8 acpi_gbl_shutdown; +extern u32 acpi_gbl_system_flags; +extern u32 acpi_gbl_startup_flags; + + +/***************************************************************************** + * + * Namespace globals + * + ****************************************************************************/ + +#define NUM_NS_TYPES INTERNAL_TYPE_INVALID+1 +#define NUM_PREDEFINED_NAMES 9 + + +ACPI_EXTERN ACPI_NAME_TABLE acpi_gbl_root_name_table; +ACPI_EXTERN ACPI_NAMED_OBJECT *acpi_gbl_root_object; + +extern u8 acpi_gbl_ns_properties[NUM_NS_TYPES]; +extern PREDEFINED_NAMES acpi_gbl_pre_defined_names [NUM_PREDEFINED_NAMES]; + + +/* Used to detect memory leaks (DEBUG ONLY) */ + +#ifdef ACPI_DEBUG +ACPI_EXTERN ALLOCATION_INFO *acpi_gbl_head_alloc_ptr; +ACPI_EXTERN ALLOCATION_INFO *acpi_gbl_tail_alloc_ptr; +#endif + + +/***************************************************************************** + * + * Interpreter globals + * + ****************************************************************************/ + + +ACPI_EXTERN u32 acpi_gbl_when_to_parse_methods; +ACPI_EXTERN ACPI_WALK_LIST *acpi_gbl_current_walk_list; + +/* Base of AML block, and pointer to current location in it */ + +ACPI_EXTERN u8 *acpi_gbl_Pcode_base; +ACPI_EXTERN u8 *acpi_gbl_Pcode; + +/* + * Length of AML block, and remaining length of current package. + */ +ACPI_EXTERN u32 acpi_gbl_Pcode_block_len; +ACPI_EXTERN u32 acpi_gbl_Pcode_len; + +ACPI_EXTERN u32 acpi_gbl_buf_seq; /* Counts allocated Buffer descriptors */ +ACPI_EXTERN s32 acpi_gbl_named_object_err; /* Indicate if inc_error should be called */ + +/* + * Handle to the last method found - used during pass1 of load + */ +ACPI_EXTERN ACPI_HANDLE acpi_gbl_last_method; + +/* + * Table of Address Space handlers + */ + +ACPI_EXTERN ACPI_ADDRESS_SPACE_INFO acpi_gbl_address_spaces[ACPI_NUM_ADDRESS_SPACES]; + + +/* Control method single step flag */ + +ACPI_EXTERN u8 acpi_gbl_cm_single_step; + + +/***************************************************************************** + * + * Parser globals + * + ****************************************************************************/ + +ACPI_EXTERN ACPI_GENERIC_OP *acpi_gbl_parsed_namespace_root; + +extern ACPI_OP_INFO acpi_gbl_aml_op_info[]; +extern u8 acpi_gbl_aml_op_info_index[256]; +extern char *acpi_gbl_parser_id; + + +/***************************************************************************** + * + * Hardware globals + * + ****************************************************************************/ + +extern ACPI_C_STATE_HANDLER acpi_hw_cx_handlers[MAX_CX_STATES]; +extern u32 acpi_hw_active_cx_state; + + +/***************************************************************************** + * + * Event globals + * + ****************************************************************************/ + +ACPI_EXTERN ACPI_FIXED_EVENT_INFO acpi_gbl_fixed_event_handlers[NUM_FIXED_EVENTS]; + +ACPI_EXTERN ACPI_HANDLE acpi_gbl_gpe_obj_handle; +ACPI_EXTERN u32 acpi_gbl_gpe_register_count; +ACPI_EXTERN ACPI_GPE_REGISTERS *acpi_gbl_gpe_registers; +ACPI_EXTERN ACPI_GPE_LEVEL_INFO *acpi_gbl_gpe_info; + +/* + * Gpe validation and translation table + * Indexed by the GPE number, returns GPE_INVALID if the GPE is not supported. + * Otherwise, returns a valid index into the global GPE table. + * + * This table is needed because the GPE numbers supported by block 1 do not + * have to be contiguous with the GPE numbers supported by block 0. + */ +ACPI_EXTERN u8 acpi_gbl_gpe_valid [NUM_GPE]; + +/* Acpi_event counter for debug only */ + +#ifdef ACPI_DEBUG +ACPI_EXTERN u32 acpi_gbl_event_count[NUM_FIXED_EVENTS]; +#endif + + +/***************************************************************************** + * + * Debugger globals + * + ****************************************************************************/ + +ACPI_EXTERN u8 acpi_gbl_method_executing; +ACPI_EXTERN u8 acpi_gbl_db_terminate_threads; + + +/* Memory allocation metrics - Debug Only! */ + +#ifdef ACPI_DEBUG + +ACPI_EXTERN u32 acpi_gbl_current_alloc_size; +ACPI_EXTERN u32 acpi_gbl_current_alloc_count; +ACPI_EXTERN u32 acpi_gbl_running_alloc_size; +ACPI_EXTERN u32 acpi_gbl_running_alloc_count; +ACPI_EXTERN u32 acpi_gbl_max_concurrent_alloc_size; +ACPI_EXTERN u32 acpi_gbl_max_concurrent_alloc_count; +ACPI_EXTERN u32 acpi_gbl_current_object_count; +ACPI_EXTERN u32 acpi_gbl_current_object_size; +ACPI_EXTERN u32 acpi_gbl_max_concurrent_object_count; +ACPI_EXTERN u32 acpi_gbl_max_concurrent_object_size; +ACPI_EXTERN u32 acpi_gbl_running_object_count; +ACPI_EXTERN u32 acpi_gbl_running_object_size; + +#endif + + +#endif /* __GLOBALS_H__ */ diff --git a/drivers/acpi/include/hardware.h b/drivers/acpi/include/hardware.h new file mode 100644 index 000000000..4191f17dd --- /dev/null +++ b/drivers/acpi/include/hardware.h @@ -0,0 +1,169 @@ + +/****************************************************************************** + * + * Name: hardware.h -- hardware specific interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __HARDWARE_H__ +#define __HARDWARE_H__ + + +/* Prototypes */ + + +ACPI_STATUS +acpi_hw_initialize( + void); + +ACPI_STATUS +acpi_hw_shutdown( + void); + +ACPI_STATUS +acpi_hw_initialize_system_info( + void); + +ACPI_STATUS +acpi_hw_set_mode ( + u32 mode); + +u32 +acpi_hw_get_mode ( + void); + +u32 +acpi_hw_get_mode_capabilities ( + void); + +/* Register I/O Prototypes */ + +u32 +acpi_hw_register_access ( + NATIVE_UINT read_write, + u8 use_lock, + u32 register_id, ... /* DWORD Value */); + +void +acpi_hw_clear_acpi_status ( + void); + + +/* GPE support */ + +void +acpi_hw_enable_gpe ( + u32 gpe_index); + +void +acpi_hw_disable_gpe ( + u32 gpe_index); + +void +acpi_hw_clear_gpe ( + u32 gpe_index); + +void +acpi_hw_get_gpe_status ( + u32 gpe_number, + ACPI_EVENT_STATUS *event_status); + +/* Sleep Prototypes */ + +ACPI_STATUS +acpi_hw_obtain_sleep_type_register_data ( + u8 sleep_state, + u8 *slp_typ_a, + u8 *slp_typ_b); + + +/* Cx State Prototypes */ + +ACPI_STATUS +acpi_hw_enter_c1( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks); + +ACPI_STATUS +acpi_hw_enter_c2( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks); + +ACPI_STATUS +acpi_hw_enter_c3( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks); + +ACPI_STATUS +acpi_hw_enter_cx ( + ACPI_IO_ADDRESS pblk_address, + u32 *pm_timer_ticks); + +ACPI_STATUS +acpi_hw_set_cx ( + u32 cx_state); + +ACPI_STATUS +acpi_hw_get_cx_info ( + u32 cx_states[]); + + +/* Throttling Prototypes */ + +void +acpi_hw_enable_throttling ( + ACPI_IO_ADDRESS pblk_address); + +void +acpi_hw_disable_throttling ( + ACPI_IO_ADDRESS pblk_address); + +u32 +acpi_hw_get_duty_cycle ( + u8 duty_offset, + ACPI_IO_ADDRESS pblk_address, + u32 num_throttle_states); + +void +acpi_hw_program_duty_cycle ( + u8 duty_offset, + u32 duty_cycle, + ACPI_IO_ADDRESS pblk_address, + u32 num_throttle_states); + +NATIVE_UINT +acpi_hw_local_pow ( + NATIVE_UINT x, + NATIVE_UINT y); + + +/* ACPI Timer prototypes */ + +u32 +acpi_hw_pmt_ticks ( + void); + +u32 +acpi_hw_pmt_resolution ( + void); + + +#endif /* __HARDWARE_H__ */ diff --git a/drivers/acpi/include/internal.h b/drivers/acpi/include/internal.h new file mode 100644 index 000000000..68c903cf7 --- /dev/null +++ b/drivers/acpi/include/internal.h @@ -0,0 +1,850 @@ + +/****************************************************************************** + * + * Name: internal.h - Internal data types used across the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ACPI_INTERNAL_H +#define _ACPI_INTERNAL_H + +#include "config.h" + + +#define WAIT_FOREVER ((u32) -1) + +typedef void* ACPI_MUTEX; +typedef u32 ACPI_MUTEX_HANDLE; + + +/* Object descriptor types */ + +#define ACPI_DESC_TYPE_INTERNAL 0xAA +#define ACPI_DESC_TYPE_PARSER 0xBB +#define ACPI_DESC_TYPE_STATE 0xCC +#define ACPI_DESC_TYPE_WALK 0xDD +#define ACPI_DESC_TYPE_NAMED 0xEE + + +/***************************************************************************** + * + * Mutex typedefs and structs + * + ****************************************************************************/ + + +/* + * Predefined handles for the mutex objects used within the subsystem + * All mutex objects are automatically created by Acpi_cm_mutex_initialize. + * NOTE: any changes here must be reflected in the Acpi_gbl_Mutex_names table also! + */ + +#define ACPI_MTX_HARDWARE 0 +#define ACPI_MTX_MEMORY 1 +#define ACPI_MTX_CACHES 2 +#define ACPI_MTX_TABLES 3 +#define ACPI_MTX_PARSER 4 +#define ACPI_MTX_DISPATCHER 5 +#define ACPI_MTX_INTERPRETER 6 +#define ACPI_MTX_EXECUTE 7 +#define ACPI_MTX_NAMESPACE 8 +#define ACPI_MTX_EVENTS 9 +#define ACPI_MTX_OP_REGIONS 10 +#define ACPI_MTX_DEBUG_CMD_READY 11 +#define ACPI_MTX_DEBUG_CMD_COMPLETE 12 + +#define MAX_MTX 12 +#define NUM_MTX MAX_MTX+1 + + +#ifdef ACPI_DEBUG +#ifdef DEFINE_ACPI_GLOBALS + +/* Names for the mutexes used in the subsystem */ + +static char *acpi_gbl_mutex_names[] = +{ + "ACPI_MTX_Hardware", + "ACPI_MTX_Memory", + "ACPI_MTX_Caches", + "ACPI_MTX_Tables", + "ACPI_MTX_Parser", + "ACPI_MTX_Dispatcher", + "ACPI_MTX_Interpreter", + "ACPI_MTX_Execute", + "ACPI_MTX_Namespace", + "ACPI_MTX_Events", + "ACPI_MTX_Op_regions", + "ACPI_MTX_Debug_cmd_ready", + "ACPI_MTX_Debug_cmd_complete" +}; + +#endif +#endif + + +/* Table for the global mutexes */ + +typedef struct acpi_mutex_info +{ + ACPI_MUTEX mutex; + u32 use_count; + u8 locked; + +} ACPI_MUTEX_INFO; + + +/* Lock flag parameter for various interfaces */ + +#define ACPI_MTX_DO_NOT_LOCK 0 +#define ACPI_MTX_LOCK 1 + + +typedef u16 ACPI_OWNER_ID; +#define OWNER_TYPE_TABLE 0x0 +#define OWNER_TYPE_METHOD 0x1 +#define FIRST_METHOD_ID 0x0000 +#define FIRST_TABLE_ID 0x8000 + +/* TBD: [Restructure] get rid of the need for this! */ + +#define TABLE_ID_DSDT (ACPI_OWNER_ID) 0xD1D1 + +/***************************************************************************** + * + * Namespace typedefs and structs + * + ****************************************************************************/ + + +/* Operational modes of the AML interpreter/scanner */ + +typedef enum +{ + IMODE_LOAD_PASS1 = 0x01, + IMODE_LOAD_PASS2 = 0x02, + IMODE_EXECUTE = 0x0E + +} OPERATING_MODE; + + +/* + * The Acpi_named_object describes a named object that appears in the AML + * An Acpi_name_table is used to store Acpi_named_objects. + * + * Data_type is used to differentiate between internal descriptors, and MUST + * be the first byte in this structure. + */ + +typedef struct acpi_named_object +{ + u8 data_type; + u8 type; /* Type associated with this name */ + u8 this_index; /* Entry number */ + u8 flags; + u32 name; /* ACPI Name, always 4 chars per ACPI spec */ + + + void *object; /* Pointer to attached ACPI object (optional) */ + struct acpi_name_table *child_table; /* Scope owned by this name (optional) */ + ACPI_OWNER_ID owner_id; /* ID of owner - either an ACPI table or a method */ + u16 reference_count; /* Current count of references and children */ + +#ifdef _IA64 + u32 fill1; /* 64-bit alignment */ +#endif + +} ACPI_NAMED_OBJECT; + + +typedef struct acpi_name_table +{ + struct acpi_name_table *next_table; + struct acpi_name_table *parent_table; + ACPI_NAMED_OBJECT *parent_entry; + ACPI_NAMED_OBJECT entries[1]; + +} ACPI_NAME_TABLE; + + +#define ENTRY_NOT_FOUND NULL + + +/* NTE flags */ + +#define NTE_AML_ATTACHMENT 0x1 + + +/* + * ACPI Table Descriptor. One per ACPI table + */ +typedef struct acpi_table_desc +{ + struct acpi_table_desc *prev; + struct acpi_table_desc *next; + struct acpi_table_desc *installed_desc; + ACPI_TABLE_HEADER *pointer; + void *base_pointer; + u8 *aml_pointer; + u32 aml_length; + u32 length; + u32 count; + ACPI_OWNER_ID table_id; + u8 type; + u8 allocation; + u8 loaded_into_namespace; + +} ACPI_TABLE_DESC; + + +typedef struct +{ + char *search_for; + ACPI_HANDLE *list; + s32 *count; + +} FIND_CONTEXT; + + +typedef struct +{ + ACPI_NAME_TABLE *name_table; + u32 position; + u8 table_full; + +} NS_SEARCH_DATA; + + +/* + * Predefined Namespace items + */ +#define ACPI_MAX_ADDRESS_SPACE 255 +#define ACPI_NUM_ADDRESS_SPACES 256 + + +typedef struct +{ + char *name; + ACPI_OBJECT_TYPE type; + char *val; + +} PREDEFINED_NAMES; + + +/***************************************************************************** + * + * Event typedefs and structs + * + ****************************************************************************/ + + +/* Status bits. */ + +#define ACPI_STATUS_PMTIMER 0x0001 +#define ACPI_STATUS_GLOBAL 0x0020 +#define ACPI_STATUS_POWER_BUTTON 0x0100 +#define ACPI_STATUS_SLEEP_BUTTON 0x0200 +#define ACPI_STATUS_RTC_ALARM 0x0400 + +/* Enable bits. */ + +#define ACPI_ENABLE_PMTIMER 0x0001 +#define ACPI_ENABLE_GLOBAL 0x0020 +#define ACPI_ENABLE_POWER_BUTTON 0x0100 +#define ACPI_ENABLE_SLEEP_BUTTON 0x0200 +#define ACPI_ENABLE_RTC_ALARM 0x0400 + + +/* + * Entry in the Address_space (AKA Operation Region) table + */ + +typedef struct +{ + ADDRESS_SPACE_HANDLER handler; + void *context; + +} ACPI_ADDRESS_SPACE_INFO; + + +/* Values and addresses of the GPE registers (both banks) */ + +typedef struct +{ + u8 status; /* Current value of status reg */ + u8 enable; /* Current value of enable reg */ + u16 status_addr; /* Address of status reg */ + u16 enable_addr; /* Address of enable reg */ + u8 gpe_base; /* Base GPE number */ + +} ACPI_GPE_REGISTERS; + + +#define ACPI_GPE_LEVEL_TRIGGERED 1 +#define ACPI_GPE_EDGE_TRIGGERED 2 + + +/* Information about each particular GPE level */ + +typedef struct +{ + u8 type; /* Level or Edge */ + + ACPI_HANDLE method_handle; /* Method handle for direct (fast) execution */ + GPE_HANDLER handler; /* Address of handler, if any */ + void *context; /* Context to be passed to handler */ + +} ACPI_GPE_LEVEL_INFO; + + +/* Information about each particular fixed event */ + +typedef struct +{ + FIXED_EVENT_HANDLER handler; /* Address of handler. */ + void *context; /* Context to be passed to handler */ + +} ACPI_FIXED_EVENT_INFO; + + +/* Information used during field processing */ + +typedef struct +{ + u8 skip_field; + u8 field_flag; + u32 pkg_length; + +} ACPI_FIELD_INFO; + + +/***************************************************************************** + * + * Parser typedefs and structs + * + ****************************************************************************/ + + +#define OP_INFO_TYPE 0x1F +#define OP_INFO_HAS_ARGS 0x20 +#define OP_INFO_CHILD_LOCATION 0xC0 + +/* + * AML opcode, name, and argument layout + */ +typedef struct acpi_op_info +{ + u16 opcode; /* AML opcode */ + u8 flags; /* Opcode type, Has_args flag */ + u32 parse_args; /* Grammar/Parse time arguments */ + u32 runtime_args; /* Interpret time arguments */ + + DEBUG_ONLY_MEMBERS ( + char *name) /* op name (debug only) */ + +} ACPI_OP_INFO; + + +typedef union acpi_op_value +{ + u32 integer; /* integer constant */ + u32 size; /* bytelist or field size */ + char *string; /* NULL terminated string */ + u8 *buffer; /* buffer or string */ + char *name; /* NULL terminated string */ + struct acpi_generic_op *arg; /* arguments and contained ops */ + ACPI_NAMED_OBJECT *entry; /* entry in interpreter namespace tbl */ + +} ACPI_OP_VALUE; + + +#define ACPI_COMMON_OP \ + u8 data_type; /* To differentiate various internal objs */\ + u8 flags; /* Type of Op */\ + u16 opcode; /* AML opcode */\ + u32 aml_offset; /* offset of declaration in AML */\ + struct acpi_generic_op *parent; /* parent op */\ + struct acpi_generic_op *next; /* next op */\ + DEBUG_ONLY_MEMBERS (\ + char op_name[16]) /* op name (debug only) */\ + /* NON-DEBUG members below: */\ + void *acpi_named_object;/* for use by interpreter */\ + ACPI_OP_VALUE value; /* Value or args associated with the opcode */\ + + +/* + * generic operation (eg. If, While, Store) + */ +typedef struct acpi_generic_op +{ + ACPI_COMMON_OP +} ACPI_GENERIC_OP; + + +/* + * operation with a name (eg. Scope, Method, Name, Named_field, ...) + */ +typedef struct acpi_named_op +{ + ACPI_COMMON_OP + u32 name; /* 4-byte name or zero if no name */ + +} ACPI_NAMED_OP; + + +/* + * special operation for methods and regions (parsing must be deferred + * until a first pass parse is completed) + */ +typedef struct acpi_deferred_op +{ + ACPI_COMMON_OP + u32 name; /* 4-byte name or 0 if none */ + u32 body_length; /* AML body size */ + u8 *body; /* AML body */ + u16 thread_count; /* Count of threads currently executing a method */ + +} ACPI_DEFERRED_OP; + + +/* + * special operation for bytelists (Byte_list only) + */ +typedef struct acpi_bytelist_op +{ + ACPI_COMMON_OP + u8 *data; /* bytelist data */ + +} ACPI_BYTELIST_OP; + + +/* + * Parse state - one state per parser invocation and each control + * method. + */ + +typedef struct acpi_parse_state +{ + u8 *aml_start; /* first AML byte */ + u8 *aml; /* next AML byte */ + u8 *aml_end; /* (last + 1) AML byte */ + u8 *pkg_end; /* current package end */ + ACPI_GENERIC_OP *start_op; /* root of parse tree */ + struct acpi_parse_scope *scope; /* current scope */ + struct acpi_parse_scope *scope_avail; /* unused (extra) scope structs */ + struct acpi_parse_state *next; + +} ACPI_PARSE_STATE; + + +/* + * Parse scope - one per ACPI scope + */ + +typedef struct acpi_parse_scope +{ + ACPI_GENERIC_OP *op; /* current op being parsed */ + u8 *arg_end; /* current argument end */ + u8 *pkg_end; /* current package end */ + struct acpi_parse_scope *parent; /* parent scope */ + u32 arg_list; /* next argument to parse */ + u32 arg_count; /* Number of fixed arguments */ + +} ACPI_PARSE_SCOPE; + + +/***************************************************************************** + * + * Generic "state" object for stacks + * + ****************************************************************************/ + + +#define CONTROL_NORMAL 0xC0 +#define CONTROL_CONDITIONAL_EXECUTING 0xC1 +#define CONTROL_PREDICATE_EXECUTING 0xC2 +#define CONTROL_PREDICATE_FALSE 0xC3 +#define CONTROL_PREDICATE_TRUE 0xC4 + + +#define ACPI_STATE_COMMON /* Two 32-bit fields and a pointer */\ + u8 data_type; /* To differentiate various internal objs */\ + u8 flags; \ + u16 value; \ + u16 state; \ + u16 acpi_eval; \ + void *next; \ + +typedef struct acpi_common_state +{ + ACPI_STATE_COMMON +} ACPI_COMMON_STATE; + + +/* + * Update state - used to traverse complex objects such as packages + */ +typedef struct acpi_update_state +{ + ACPI_STATE_COMMON + union acpi_obj_internal *object; + +} ACPI_UPDATE_STATE; + +/* + * Control state - one per if/else and while constructs. + * Allows nesting of these constructs + */ +typedef struct acpi_control_state +{ + ACPI_STATE_COMMON + ACPI_GENERIC_OP *predicate_op; /* Start of if/while predicate */ + +} ACPI_CONTROL_STATE; + + +/* + * Scope state - current scope during namespace lookups + */ + +typedef struct acpi_scope_state +{ + ACPI_STATE_COMMON + ACPI_NAME_TABLE *name_table; + +} ACPI_SCOPE_STATE; + + +typedef union acpi_gen_state +{ + ACPI_COMMON_STATE common; + ACPI_CONTROL_STATE control; + ACPI_UPDATE_STATE update; + ACPI_SCOPE_STATE scope; + +} ACPI_GENERIC_STATE; + + +/***************************************************************************** + * + * Tree walking typedefs and structs + * + ****************************************************************************/ + + +/* + * Walk state - current state of a parse tree walk. Used for both a leisurely stroll through + * the tree (for whatever reason), and for control method execution. + */ + +#define NEXT_OP_DOWNWARD 1 +#define NEXT_OP_UPWARD 2 + +typedef struct acpi_walk_state +{ + u8 data_type; /* To differentiate various internal objs */\ + ACPI_OWNER_ID owner_id; /* Owner of objects created during the walk */ + u8 last_predicate; /* Result of last predicate */ + u8 next_op_info; /* Info about Next_op */ + u8 num_operands; /* Stack pointer for Operands[] array */ + u8 num_results; /* Stack pointer for Results[] array */ + u8 current_result; /* */ + + struct acpi_walk_state *next; /* Next Walk_state in list */ + ACPI_GENERIC_OP *origin; /* Start of walk */ + ACPI_GENERIC_OP *prev_op; /* Last op that was processed */ + ACPI_GENERIC_OP *next_op; /* next op to be processed */ + ACPI_GENERIC_STATE *control_state; /* List of control states (nested IFs) */ + ACPI_GENERIC_STATE *scope_info; /* Stack of nested scopes */ + union acpi_obj_internal *return_desc; /* Return object, if any */ + union acpi_obj_internal *method_desc; /* Method descriptor if running a method */ + ACPI_GENERIC_OP *method_call_op; /* Method_call Op if running a method */ + union acpi_obj_internal *operands[OBJ_NUM_OPERANDS]; /* Operands passed to the interpreter */ + union acpi_obj_internal *results[OBJ_NUM_OPERANDS]; /* Accumulated results */ + struct acpi_named_object arguments[MTH_NUM_ARGS]; /* Control method arguments */ + struct acpi_named_object local_variables[MTH_NUM_LOCALS]; /* Control method locals */ + + +} ACPI_WALK_STATE; + + +/* + * Walk list - head of a tree of walk states. Multiple walk states are created when there + * are nested control methods executing. + */ +typedef struct acpi_walk_list +{ + + ACPI_WALK_STATE *walk_state; + +} ACPI_WALK_LIST; + + +typedef +ACPI_STATUS (*INTERPRETER_CALLBACK) ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op); + + +/* Info used by Acpi_ps_init_objects */ + +typedef struct init_walk_info +{ + u32 method_count; + u32 op_region_count; + ACPI_TABLE_DESC *table_desc; + +} INIT_WALK_INFO; + + +/* TBD: [Restructure] Merge with struct above */ + +typedef struct acpi_walk_info +{ + u32 debug_level; + u32 owner_id; + +} ACPI_WALK_INFO; + + +/***************************************************************************** + * + * Hardware and PNP + * + ****************************************************************************/ + + +/* Sleep states */ + +#define SLWA_DEBUG_LEVEL 4 +#define GTS_CALL 0 +#define GTS_WAKE 1 + +/* Cx States */ + +#define MAX_CX_STATE_LATENCY 0xFFFFFFFF +#define MAX_CX_STATES 4 + +/* + * The #define's and enum below establish an abstract way of identifying what + * register block and register is to be accessed. Do not change any of the + * values as they are used in switch statements and offset calculations. + */ + +#define REGISTER_BLOCK_MASK 0xFF00 +#define BIT_IN_REGISTER_MASK 0x00FF +#define PM1_EVT 0x0100 +#define PM1_CONTROL 0x0200 +#define PM2_CONTROL 0x0300 +#define PM_TIMER 0x0400 +#define PROCESSOR_BLOCK 0x0500 +#define GPE0_STS_BLOCK 0x0600 +#define GPE0_EN_BLOCK 0x0700 +#define GPE1_STS_BLOCK 0x0800 +#define GPE1_EN_BLOCK 0x0900 + +enum +{ + /* PM1 status register ids */ + + TMR_STS = (PM1_EVT | 0x01), + BM_STS, + GBL_STS, + PWRBTN_STS, + SLPBTN_STS, + RTC_STS, + WAK_STS, + + /* PM1 enable register ids */ + + TMR_EN, + /* need to skip 1 enable number since there's no bus master enable register */ + GBL_EN = (PM1_EVT | 0x0A), + PWRBTN_EN, + SLPBTN_EN, + RTC_EN, + + /* PM1 control register ids */ + + SCI_EN = (PM1_CONTROL | 0x01), + BM_RLD, + GBL_RLS, + SLP_TYPE_A, + SLP_TYPE_B, + SLP_EN, + + /* PM2 control register ids */ + + ARB_DIS = (PM2_CONTROL | 0x01), + + /* PM Timer register ids */ + + TMR_VAL = (PM_TIMER | 0x01), + + GPE0_STS = (GPE0_STS_BLOCK | 0x01), + GPE0_EN = (GPE0_EN_BLOCK | 0x01), + + GPE1_STS = (GPE1_STS_BLOCK | 0x01), + GPE1_EN = (GPE0_EN_BLOCK | 0x01), + + /* Last register value is one less than LAST_REG */ + + LAST_REG +}; + + +#define TMR_STS_MASK 0x0001 +#define BM_STS_MASK 0x0010 +#define GBL_STS_MASK 0x0020 +#define PWRBTN_STS_MASK 0x0100 +#define SLPBTN_STS_MASK 0x0200 +#define RTC_STS_MASK 0x0400 +#define WAK_STS_MASK 0x8000 + +#define ALL_FIXED_STS_BITS (TMR_STS_MASK | BM_STS_MASK | GBL_STS_MASK | PWRBTN_STS_MASK | \ + SLPBTN_STS_MASK | RTC_STS_MASK | WAK_STS_MASK) + +#define TMR_EN_MASK 0x0001 +#define GBL_EN_MASK 0x0020 +#define PWRBTN_EN_MASK 0x0100 +#define SLPBTN_EN_MASK 0x0200 +#define RTC_EN_MASK 0x0400 + +#define SCI_EN_MASK 0x0001 +#define BM_RLD_MASK 0x0002 +#define GBL_RLS_MASK 0x0004 +#define SLP_TYPE_X_MASK 0x1C00 +#define SLP_EN_MASK 0x2000 + +#define ARB_DIS_MASK 0x0001 + +#define GPE0_STS_MASK +#define GPE0_EN_MASK + +#define GPE1_STS_MASK +#define GPE1_EN_MASK + + +#define ACPI_READ 1 +#define ACPI_WRITE 2 + +#define LOW_BYTE 0x00FF +#define ONE_BYTE 0x08 + +#ifndef SET + #define SET 1 +#endif +#ifndef CLEAR + #define CLEAR 0 +#endif + + +/* Plug and play */ + +/* Pnp and ACPI data */ + +#define VERSION_NO 0x01 +#define LOGICAL_DEVICE_ID 0x02 +#define COMPATIBLE_DEVICE_ID 0x03 +#define IRQ_FORMAT 0x04 +#define DMA_FORMAT 0x05 +#define START_DEPENDENT_TAG 0x06 +#define END_DEPENDENT_TAG 0x07 +#define IO_PORT_DESCRIPTOR 0x08 +#define FIXED_LOCATION_IO_DESCRIPTOR 0x09 +#define RESERVED_TYPE0 0x0A +#define RESERVED_TYPE1 0x0B +#define RESERVED_TYPE2 0x0C +#define RESERVED_TYPE3 0x0D +#define SMALL_VENDOR_DEFINED 0x0E +#define END_TAG 0x0F + +/* Pnp and ACPI data */ + +#define MEMORY_RANGE_24 0x81 +#define ISA_MEMORY_RANGE 0x81 +#define LARGE_VENDOR_DEFINED 0x84 +#define EISA_MEMORY_RANGE 0x85 +#define MEMORY_RANGE_32 0x85 +#define FIXED_EISA_MEMORY_RANGE 0x86 +#define FIXED_MEMORY_RANGE_32 0x86 + +/* ACPI only data */ + +#define DWORD_ADDRESS_SPACE 0x87 +#define WORD_ADDRESS_SPACE 0x88 +#define EXTENDED_IRQ 0x89 + +/* MUST HAVES */ + + +typedef enum +{ + DWORD_DEVICE_ID, + STRING_PTR_DEVICE_ID, + STRING_DEVICE_ID + +} DEVICE_ID_TYPE; + +typedef struct +{ + DEVICE_ID_TYPE type; + union + { + u32 number; + char *string_ptr; + char buffer[9]; + } data; + +} DEVICE_ID; + + +/***************************************************************************** + * + * Debug + * + ****************************************************************************/ + + +/* Entry for a memory allocation (debug only) */ + +#ifdef ACPI_DEBUG + +#define MEM_MALLOC 0 +#define MEM_CALLOC 1 +#define MAX_MODULE_NAME 16 + +typedef struct allocation_info +{ + struct allocation_info *previous; + struct allocation_info *next; + void *address; + u32 size; + u32 component; + u32 line; + char module[MAX_MODULE_NAME]; + u8 alloc_type; + +} ALLOCATION_INFO; + +#endif + +#endif diff --git a/drivers/acpi/include/interp.h b/drivers/acpi/include/interp.h new file mode 100644 index 000000000..75db528da --- /dev/null +++ b/drivers/acpi/include/interp.h @@ -0,0 +1,660 @@ + +/****************************************************************************** + * + * Name: interp.h - Interpreter subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __INTERP_H__ +#define __INTERP_H__ + + +#include "actypes.h" +#include "acobject.h" + + +#define WALK_OPERANDS &(walk_state->operands [walk_state->num_operands -1]) + + +/* Interpreter constants */ + +#define AML_END_OF_BLOCK -1 +#define PUSH_PKG_LENGTH 1 +#define DO_NOT_PUSH_PKG_LENGTH 0 + + +#define STACK_TOP 0 +#define STACK_BOTTOM (u32) -1 + +/* Constants for global "When_to_parse_methods" */ + +#define METHOD_PARSE_AT_INIT 0x0 +#define METHOD_PARSE_JUST_IN_TIME 0x1 +#define METHOD_DELETE_AT_COMPLETION 0x2 + + +ACPI_STATUS +acpi_aml_resolve_operands ( + u16 opcode, + ACPI_OBJECT_INTERNAL **stack_ptr); + + +/* + * amxface - External interpreter interfaces + */ + +ACPI_STATUS +acpi_aml_load_table ( + ACPI_TABLE_TYPE table_id); + +ACPI_STATUS +acpi_aml_execute_method ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc); + + +/* + * amcopy - Interpreter object copy support + */ + +ACPI_STATUS +acpi_aml_build_copy_internal_package_object ( + ACPI_OBJECT_INTERNAL *source_obj, + ACPI_OBJECT_INTERNAL *dest_obj); + + +/* + * amfield - ACPI AML (p-code) execution - field manipulation + */ + + +ACPI_STATUS +acpi_aml_read_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + void *buffer, + u32 buffer_length, + u32 byte_length, + u32 datum_length, + u32 bit_granularity, + u32 byte_granularity); + +ACPI_STATUS +acpi_aml_write_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + void *buffer, + u32 buffer_length, + u32 byte_length, + u32 datum_length, + u32 bit_granularity, + u32 byte_granularity); + +ACPI_STATUS +acpi_aml_setup_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + ACPI_OBJECT_INTERNAL *rgn_desc, + s32 field_bit_width); + +ACPI_STATUS +acpi_aml_read_field_data ( + ACPI_OBJECT_INTERNAL *obj_desc, + u32 field_byte_offset, + u32 field_bit_width, + u32 *value); + +ACPI_STATUS +acpi_aml_access_named_field ( + s32 mode, + ACPI_HANDLE named_field, + void *buffer, + u32 length); + +ACPI_STATUS +acpi_aml_set_named_field_value ( + ACPI_HANDLE named_field, + void *buffer, + u32 length); + +ACPI_STATUS +acpi_aml_get_named_field_value ( + ACPI_HANDLE named_field, + void *buffer, + u32 length); + + +/* + * ammisc - ACPI AML (p-code) execution - specific opcodes + */ + +ACPI_STATUS +acpi_aml_exec_create_field ( + u16 opcode, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_reconfiguration ( + u16 opcode, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_fatal ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_index ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + +ACPI_STATUS +acpi_aml_exec_match ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + +ACPI_STATUS +acpi_aml_exec_create_mutex ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_create_processor ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE processor_nTE); + +ACPI_STATUS +acpi_aml_exec_create_power_resource ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE processor_nTE); + +ACPI_STATUS +acpi_aml_exec_create_region ( + u8 *aml_ptr, + u32 acpi_aml_length, + u32 region_space, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_create_event ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_create_alias ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_create_method ( + u8 *aml_ptr, + u32 acpi_aml_length, + u32 method_flags, + ACPI_HANDLE method); + + +/* + * amprep - ACPI AML (p-code) execution - prep utilities + */ + +ACPI_STATUS +acpi_aml_prep_def_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE region, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length); + +ACPI_STATUS +acpi_aml_prep_bank_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE region, + ACPI_HANDLE bank_reg, + u32 bank_val, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length); + +ACPI_STATUS +acpi_aml_prep_index_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE index_reg, + ACPI_HANDLE data_reg, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length); + +ACPI_STATUS +acpi_aml_prep_operands ( + char *types, + ACPI_OBJECT_INTERNAL **stack_ptr); + + +/* + * iepstack - package stack utilities + */ + +/* +u32 +Acpi_aml_pkg_stack_level ( + void); + +void +Acpi_aml_clear_pkg_stack ( + void); + +ACPI_STATUS +Acpi_aml_pkg_push_length ( + u32 Length, + OPERATING_MODE Load_exec_mode); + +ACPI_STATUS +Acpi_aml_pkg_push_exec_length ( + u32 Length); + +ACPI_STATUS +Acpi_aml_pkg_push_exec ( + u8 *Code, + u32 Len); + +ACPI_STATUS +Acpi_aml_pkg_pop_length ( + s32 No_err_under, + OPERATING_MODE Load_exec_mode); + +ACPI_STATUS +Acpi_aml_pkg_pop_exec_length ( + void); + +ACPI_STATUS +Acpi_aml_pkg_pop_exec ( + void); + +*/ + +/* + * amsystem - Interface to OS services + */ + +u16 +acpi_aml_system_thread_id ( + void); + +ACPI_STATUS +acpi_aml_system_do_notify_op ( + ACPI_OBJECT_INTERNAL *value, + ACPI_OBJECT_INTERNAL *obj_desc); + +void +acpi_aml_system_do_suspend( + u32 time); + +void +acpi_aml_system_do_stall ( + u32 time); + +ACPI_STATUS +acpi_aml_system_acquire_mutex( + ACPI_OBJECT_INTERNAL *time, + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_aml_system_release_mutex( + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_aml_system_signal_event( + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_aml_system_wait_event( + ACPI_OBJECT_INTERNAL *time, + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_aml_system_reset_event( + ACPI_OBJECT_INTERNAL *obj_desc); + +ACPI_STATUS +acpi_aml_system_wait_semaphore ( + ACPI_HANDLE semaphore, + u32 timeout); + + +/* + * ammonadic - ACPI AML (p-code) execution, monadic operators + */ + +ACPI_STATUS +acpi_aml_exec_monadic1 ( + u16 opcode, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_monadic2 ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + +ACPI_STATUS +acpi_aml_exec_monadic2_r ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + + +/* + * amdyadic - ACPI AML (p-code) execution, dyadic operators + */ + +ACPI_STATUS +acpi_aml_exec_dyadic1 ( + u16 opcode, + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_aml_exec_dyadic2 ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + +ACPI_STATUS +acpi_aml_exec_dyadic2_r ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + +ACPI_STATUS +acpi_aml_exec_dyadic2_s ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc); + + +/* + * amresolv - Object resolution and get value functions + */ + +ACPI_STATUS +acpi_aml_resolve_to_value ( + ACPI_OBJECT_INTERNAL **stack_ptr); + +ACPI_STATUS +acpi_aml_resolve_entry_to_value ( + ACPI_NAMED_OBJECT **stack_ptr); + +ACPI_STATUS +acpi_aml_resolve_object_to_value ( + ACPI_OBJECT_INTERNAL **stack_ptr); + +ACPI_STATUS +acpi_aml_get_field_unit_value ( + ACPI_OBJECT_INTERNAL *field_desc, + ACPI_OBJECT_INTERNAL *result_desc); + + +/* + * amcode - Scanner AML code manipulation routines + */ + +s32 +acpi_aml_avail ( + ACPI_SIZE n); + +s32 +acpi_aml_peek ( + void); + +s32 +acpi_aml_get_pcode_byte ( + u8 *pcode); + +u16 +acpi_aml_peek_op ( + void); + +u8 * +acpi_aml_consume_bytes ( + ACPI_SIZE bytes); + +ACPI_SIZE +acpi_aml_consume_stream_bytes ( + ACPI_SIZE bytes_to_get, + u8 *aml_buffer); + +void +acpi_aml_consume_package ( + OPERATING_MODE load_exec_mode); + +void +acpi_aml_set_pcode_input ( + u8 *base, + u32 length); + +ACPI_STATUS +acpi_aml_set_method ( + void *object); + +ACPI_STATUS +acpi_aml_prep_exec ( + u8 *pcode, + u32 pcode_length); + +ACPI_HANDLE +acpi_aml_get_pcode_handle ( + void); + +void +acpi_aml_get_current_location ( + ACPI_OBJECT_INTERNAL *method_desc); + +void +acpi_aml_set_current_location ( + ACPI_OBJECT_INTERNAL *method_desc); + + +/* + * amdump - Scanner debug output routines + */ + +void +acpi_aml_show_hex_value ( + s32 byte_count, + u8 *aml_ptr, + s32 lead_space); + +void +acpi_aml_dump_buffer ( + ACPI_SIZE length); + + +ACPI_STATUS +acpi_aml_dump_operand ( + ACPI_OBJECT_INTERNAL *entry_desc); + +void +acpi_aml_dump_operands ( + ACPI_OBJECT_INTERNAL **operands, + OPERATING_MODE interpreter_mode, + char *ident, + s32 num_levels, + char *note, + char *module_name, + s32 line_number); + +void +acpi_aml_dump_object_descriptor ( + ACPI_OBJECT_INTERNAL *object, + u32 flags); + + +void +acpi_aml_dump_acpi_named_object ( + ACPI_NAMED_OBJECT *entry, + u32 flags); + + +/* + * amnames - interpreter/scanner name load/execute + */ + +char * +acpi_aml_allocate_name_string ( + u32 prefix_count, + u32 num_name_segs); + +s32 +acpi_aml_good_char ( + s32 character); + +ACPI_STATUS +acpi_aml_exec_name_segment ( + u8 **in_aml_address, + char *name_string); + +ACPI_STATUS +acpi_aml_get_name_string ( + OBJECT_TYPE_INTERNAL data_type, + u8 *in_aml_address, + char **out_name_string, + u32 *out_name_length); + +u32 +acpi_aml_decode_package_length ( + u32 last_pkg_len); + + +ACPI_STATUS +acpi_aml_do_name ( + ACPI_OBJECT_TYPE data_type, + OPERATING_MODE load_exec_mode); + + +/* + * amstore - Object store support + */ + +ACPI_STATUS +acpi_aml_exec_store ( + ACPI_OBJECT_INTERNAL *op1, + ACPI_OBJECT_INTERNAL *res); + +ACPI_STATUS +acpi_aml_store_object_to_object ( + ACPI_OBJECT_INTERNAL *val_desc, + ACPI_OBJECT_INTERNAL *dest_desc); + +ACPI_STATUS +acpi_aml_store_object_to_nte ( + ACPI_OBJECT_INTERNAL *val_desc, + ACPI_NAMED_OBJECT *entry); + + +/* + * amutils - interpreter/scanner utilities + */ + +void +acpi_aml_enter_interpreter ( + void); + +void +acpi_aml_exit_interpreter ( + void); + +u8 +acpi_aml_validate_object_type ( + ACPI_OBJECT_TYPE type); + +u8 +acpi_aml_acquire_global_lock ( + u32 rule); + +ACPI_STATUS +acpi_aml_release_global_lock ( + u8 locked); + +void +acpi_aml_append_operand_diag( + char *name, + s32 line, + u16 op_code, + ACPI_OBJECT_INTERNAL **operands, + s32 Noperands); + +u32 +acpi_aml_buf_seq ( + void); + +s32 +acpi_aml_digits_needed ( + s32 value, + s32 base); + +ACPI_STATUS +acpi_aml_eisa_id_to_string ( + u32 numeric_id, + char *out_string); + + +/* + * amregion - default Op_region handlers + */ + +ACPI_STATUS +acpi_aml_system_memory_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + +ACPI_STATUS +acpi_aml_system_io_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + +ACPI_STATUS +acpi_aml_pci_config_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + +ACPI_STATUS +acpi_aml_embedded_controller_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + +ACPI_STATUS +acpi_aml_sm_bus_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context); + + +#endif /* __INTERP_H__ */ diff --git a/drivers/acpi/include/macros.h b/drivers/acpi/include/macros.h new file mode 100644 index 000000000..7584ee8fa --- /dev/null +++ b/drivers/acpi/include/macros.h @@ -0,0 +1,423 @@ + +/****************************************************************************** + * + * Name: macros.h - C macros for the entire subsystem. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MACROS_H__ +#define __MACROS_H__ + +/* + * Data manipulation macros + */ + +#ifndef LOWORD +#define LOWORD(l) ((u16)(NATIVE_UINT)(l)) +#endif + +#ifndef HIWORD +#define HIWORD(l) ((u16)((((NATIVE_UINT)(l)) >> 16) & 0xFFFF)) +#endif + +#ifndef LOBYTE +#define LOBYTE(l) ((u8)(u16)(l)) +#endif + +#ifndef HIBYTE +#define HIBYTE(l) ((u8)((((u16)(l)) >> 8) & 0xFF)) +#endif + +#define BIT0(x) ((((x) & 0x01) > 0) ? 1 : 0) +#define BIT1(x) ((((x) & 0x02) > 0) ? 1 : 0) +#define BIT2(x) ((((x) & 0x04) > 0) ? 1 : 0) + +#define BIT3(x) ((((x) & 0x08) > 0) ? 1 : 0) +#define BIT4(x) ((((x) & 0x10) > 0) ? 1 : 0) +#define BIT5(x) ((((x) & 0x20) > 0) ? 1 : 0) +#define BIT6(x) ((((x) & 0x40) > 0) ? 1 : 0) +#define BIT7(x) ((((x) & 0x80) > 0) ? 1 : 0) + +#define LOW_BASE(w) ((u16) ((w) & 0x0000FFFF)) +#define MID_BASE(b) ((u8) (((b) & 0x00FF0000) >> 16)) +#define HI_BASE(b) ((u8) (((b) & 0xFF000000) >> 24)) +#define LOW_LIMIT(w) ((u16) ((w) & 0x0000FFFF)) +#define HI_LIMIT(b) ((u8) (((b) & 0x00FF0000) >> 16)) + + + /* + * Extract a byte of data using a pointer. Any more than a byte and we + * get into potential aligment issues -- see the STORE macros below + */ +#define GET8(addr) (*(u8*)(addr)) + + +/* + * Macros for moving data around to/from buffers that are possibly unaligned. + * If the hardware supports the transfer of unaligned data, just do the store. + * Otherwise, we have to move one byte at a time. + */ + +#ifdef _HW_ALIGNMENT_SUPPORT + +/* The hardware supports unaligned transfers, just do the move */ + +#define MOVE_UNALIGNED16_TO_16(d,s) *(u16*)(d) = *(u16*)(s) +#define MOVE_UNALIGNED32_TO_32(d,s) *(u32*)(d) = *(u32*)(s) +#define MOVE_UNALIGNED16_TO_32(d,s) *(u32*)(d) = *(u16*)(s) + +#else +/* + * The hardware does not support unaligned transfers. We must move the + * data one byte at a time. These macros work whether the source or + * the destination (or both) is/are unaligned. + */ + +#define MOVE_UNALIGNED16_TO_16(d,s) {((char *)(d))[0] = ((char *)(s))[0];\ + ((char *)(d))[1] = ((char *)(s))[1];} + +#define MOVE_UNALIGNED32_TO_32(d,s) {((char *)(d))[0] = ((char *)(s))[0];\ + ((char *)(d))[1] = ((char *)(s))[1];\ + ((char *)(d))[2] = ((char *)(s))[2];\ + ((char *)(d))[3] = ((char *)(s))[3];} + +#define MOVE_UNALIGNED16_TO_32(d,s) {(*(u32*)(d)) = 0; MOVE_UNALIGNED16_TO_16(d,s);} + +#endif + + +/* + * Fast power-of-two math macros for non-optimized compilers + */ + +#define _DIV(value,power_of2) ((value) >> (power_of2)) +#define _MUL(value,power_of2) ((value) << (power_of2)) +#define _MOD(value,divisor) ((value) & ((divisor) -1)) + +#define DIV_2(a) _DIV(a,1) +#define MUL_2(a) _MUL(a,1) +#define MOD_2(a) _MOD(a,2) + +#define DIV_4(a) _DIV(a,2) +#define MUL_4(a) _MUL(a,2) +#define MOD_4(a) _MOD(a,4) + +#define DIV_8(a) _DIV(a,3) +#define MUL_8(a) _MUL(a,3) +#define MOD_8(a) _MOD(a,8) + +#define DIV_16(a) _DIV(a,4) +#define MUL_16(a) _MUL(a,4) +#define MOD_16(a) _MOD(a,16) + + +/* + * Rounding macros (Power of two boundaries only) + */ + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) +#define ROUND_UP(value,boundary) (((value) + ((boundary)-1)) & (~((boundary)-1))) + +#define ROUND_DOWN_TO_32_BITS(a) ROUND_DOWN(a,4) +#define ROUND_DOWN_TO_NATIVE_WORD(a) ROUND_DOWN(a,ALIGNED_ADDRESS_BOUNDARY) + +#define ROUND_UP_TO_32_bITS(a) ROUND_UP(a,4) +#define ROUND_UP_TO_NATIVE_WORD(a) ROUND_UP(a,ALIGNED_ADDRESS_BOUNDARY) + + +#ifdef DEBUG_ASSERT +#undef DEBUG_ASSERT +#endif + + +/* + * An ACPI_HANDLE (which is actually an ACPI_NAMED_OBJECT*) can appear in some contexts, + * such as on ap_obj_stack, where a pointer to an ACPI_OBJECT_INTERNAL can also + * appear. This macro is used to distinguish them. + * + * The Data_type field is the first field in both structures. + */ + +#define VALID_DESCRIPTOR_TYPE(d,t) (((ACPI_NAMED_OBJECT*)d)->data_type == t) + + +/* Macro to test the object type */ + +#define IS_THIS_OBJECT_TYPE(d,t) (((ACPI_OBJECT_INTERNAL *)d)->common.type == (u8)t) + + +/* + * Macro to check if a pointer is within an ACPI table. + * Parameter (a) is the pointer to check. Parameter (b) must be defined + * as a pointer to an ACPI_TABLE_HEADER. (b+1) then points past the header, + * and ((u8 *)b+b->Length) points one byte past the end of the table. + */ + +#ifndef _IA16 +#define IS_IN_ACPI_TABLE(a,b) (((u8 *)(a) >= (u8 *)(b + 1)) &&\ + ((u8 *)(a) < ((u8 *)b + b->length))) + +#else +#define IS_IN_ACPI_TABLE(a,b) (_segment)(a) == (_segment)(b) &&\ + (((u8 *)(a) >= (u8 *)(b + 1)) &&\ + ((u8 *)(a) < ((u8 *)b + b->length))) +#endif + +/* + * Macros for the master AML opcode table + */ + +#ifdef ACPI_DEBUG +#define OP_INFO_ENTRY(opcode,flags,name,Pargs,Iargs) {opcode,flags,Pargs,Iargs,name} +#else +#define OP_INFO_ENTRY(opcode,flags,name,Pargs,Iargs) {opcode,flags,Pargs,Iargs} +#endif + +#define ARG_TYPE_WIDTH 5 +#define ARG_1(x) ((u32)(x)) +#define ARG_2(x) ((u32)(x) << (1 * ARG_TYPE_WIDTH)) +#define ARG_3(x) ((u32)(x) << (2 * ARG_TYPE_WIDTH)) +#define ARG_4(x) ((u32)(x) << (3 * ARG_TYPE_WIDTH)) +#define ARG_5(x) ((u32)(x) << (4 * ARG_TYPE_WIDTH)) +#define ARG_6(x) ((u32)(x) << (5 * ARG_TYPE_WIDTH)) + +#define ARGI_LIST1(a) (ARG_1(a)) +#define ARGI_LIST2(a,b) (ARG_1(b)|ARG_2(a)) +#define ARGI_LIST3(a,b,c) (ARG_1(c)|ARG_2(b)|ARG_3(a)) +#define ARGI_LIST4(a,b,c,d) (ARG_1(d)|ARG_2(c)|ARG_3(b)|ARG_4(a)) +#define ARGI_LIST5(a,b,c,d,e) (ARG_1(e)|ARG_2(d)|ARG_3(c)|ARG_4(b)|ARG_5(a)) +#define ARGI_LIST6(a,b,c,d,e,f) (ARG_1(f)|ARG_2(e)|ARG_3(d)|ARG_4(c)|ARG_5(b)|ARG_6(a)) + +#define ARGP_LIST1(a) (ARG_1(a)) +#define ARGP_LIST2(a,b) (ARG_1(a)|ARG_2(b)) +#define ARGP_LIST3(a,b,c) (ARG_1(a)|ARG_2(b)|ARG_3(c)) +#define ARGP_LIST4(a,b,c,d) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)) +#define ARGP_LIST5(a,b,c,d,e) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)) +#define ARGP_LIST6(a,b,c,d,e,f) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)|ARG_6(f)) + +#define GET_CURRENT_ARG_TYPE(list) (list & 0x1F) +#define INCREMENT_ARG_LIST(list) (list >>= ARG_TYPE_WIDTH) + + +/* + * Reporting macros that are never compiled out + */ + +/* + * Error reporting. These versions add callers module and line#. Since + * _THIS_MODULE gets compiled out when ACPI_DEBUG isn't defined, only + * use it in debug mode. + */ + +#ifdef ACPI_DEBUG + +#define REPORT_INFO(a) _report_info(_THIS_MODULE,__LINE__,_COMPONENT,a) +#define REPORT_ERROR(a) _report_error(_THIS_MODULE,__LINE__,_COMPONENT,a) +#define REPORT_WARNING(a) _report_warning(_THIS_MODULE,__LINE__,_COMPONENT,a) +#define REPORT_SUCCESS(a) _report_success(_THIS_MODULE,__LINE__,_COMPONENT,a) + +#else + +#define REPORT_INFO(a) _report_info("",__LINE__,_COMPONENT,a) +#define REPORT_ERROR(a) _report_error("",__LINE__,_COMPONENT,a) +#define REPORT_WARNING(a) _report_warning("",__LINE__,_COMPONENT,a) +#define REPORT_SUCCESS(a) _report_success("",__LINE__,_COMPONENT,a) + +#endif + +/* Error reporting. These versions pass thru the module and line# */ + +#define _REPORT_INFO(a,b,c,d) _report_info(a,b,c,d) +#define _REPORT_ERROR(a,b,c,d) _report_error(a,b,c,d) +#define _REPORT_WARNING(a,b,c,d) _report_warning(a,b,c,d) + +/* Buffer dump macros */ + +#define DUMP_BUFFER(a,b) acpi_cm_dump_buffer((char *)a,b,DB_BYTE_DISPLAY,_COMPONENT) + +/* + * Debug macros that are conditionally compiled + */ + +#ifdef ACPI_DEBUG + +#define MODULE_NAME(name) static char *_THIS_MODULE = name + +/* + * Function entry tracing. + * The first parameter should be the procedure name as a quoted string. This is declared + * as a local string ("_Proc_name) so that it can be also used by the function exit macros below. + */ + +#define FUNCTION_TRACE(a) char * _proc_name = a;\ + function_trace(_THIS_MODULE,__LINE__,_COMPONENT,a) +#define FUNCTION_TRACE_PTR(a,b) char * _proc_name = a;\ + function_trace_ptr(_THIS_MODULE,__LINE__,_COMPONENT,a,(void *)b) +#define FUNCTION_TRACE_U32(a,b) char * _proc_name = a;\ + function_trace_u32(_THIS_MODULE,__LINE__,_COMPONENT,a,(u32)b) +#define FUNCTION_TRACE_STR(a,b) char * _proc_name = a;\ + function_trace_str(_THIS_MODULE,__LINE__,_COMPONENT,a,(char *)b) +/* + * Function exit tracing. + * WARNING: These macros include a return statement. This is usually considered + * bad form, but having a separate exit macro is very ugly and difficult to maintain. + * One of the FUNCTION_TRACE macros above must be used in conjunction with these macros + * so that "_Proc_name" is defined. + */ +#define return_VOID {function_exit(_THIS_MODULE,__LINE__,_COMPONENT,_proc_name);return;} +#define return_ACPI_STATUS(s) {function_status_exit(_THIS_MODULE,__LINE__,_COMPONENT,_proc_name,s);return(s);} +#define return_VALUE(s) {function_value_exit(_THIS_MODULE,__LINE__,_COMPONENT,_proc_name,(NATIVE_UINT)s);return(s);} +#define return_PTR(s) {function_ptr_exit(_THIS_MODULE,__LINE__,_COMPONENT,_proc_name,(char *)s);return(s);} + + +/* Conditional execution */ + +#define DEBUG_EXEC(a) a; +#define NORMAL_EXEC(a) + +#define DEBUG_DEFINE(a) a; +#define DEBUG_ONLY_MEMBERS(a) a; + + +/* Stack and buffer dumping */ + +#define DUMP_STACK_ENTRY(a) acpi_aml_dump_operand(a) +#define DUMP_OPERANDS(a,b,c,d,e) acpi_aml_dump_operands(a,b,c,d,e,_THIS_MODULE,__LINE__) + + +#define DUMP_ENTRY(a,b) acpi_ns_dump_entry (a,b) +#define DUMP_TABLES(a,b) acpi_ns_dump_tables(a,b) +#define DUMP_PATHNAME(a,b,c,d) acpi_ns_dump_pathname(a,b,c,d) +#define BREAK_MSG(a) acpi_os_breakpoint (a) + +/* + * Generate INT3 on ACPI_ERROR (Debug only!) + */ + +#define ERROR_BREAK +#ifdef ERROR_BREAK +#define BREAK_ON_ERROR(lvl) if ((lvl)&ACPI_ERROR) acpi_os_breakpoint("Fatal error encountered\n") +#else +#define BREAK_ON_ERROR(lvl) +#endif + +/* + * Master debug print macros + * Print iff: + * 1) Debug print for the current component is enabled + * 2) Debug error level or trace level for the print statement is enabled + * + */ + +#define PARAM_LIST(pl) pl + +#define TEST_DEBUG_SWITCH(lvl) if (((lvl) & acpi_dbg_level) && (_COMPONENT & acpi_dbg_layer)) + +#define DEBUG_PRINT(lvl,fp) TEST_DEBUG_SWITCH(lvl) {\ + debug_print_prefix (_THIS_MODULE,__LINE__);\ + debug_print_raw PARAM_LIST(fp);\ + BREAK_ON_ERROR(lvl);} + +#define DEBUG_PRINT_RAW(lvl,fp) TEST_DEBUG_SWITCH(lvl) {\ + debug_print_raw PARAM_LIST(fp);} + + +/* Assert macros */ + +#define ACPI_ASSERT(exp) if(!(exp)) \ + acpi_os_dbg_assert(#exp, __FILE__, __LINE__, "Failed Assertion") + +#define DEBUG_ASSERT(msg, exp) if(!(exp)) \ + acpi_os_dbg_assert(#exp, __FILE__, __LINE__, msg) + + +#else +/* + * This is the non-debug case -- make everything go away, + * leaving no executable debug code! + */ + +#define MODULE_NAME(name) +#define _THIS_MODULE "" + +#define DEBUG_EXEC(a) +#define NORMAL_EXEC(a) a; + +#define DEBUG_DEFINE(a) +#define DEBUG_ONLY_MEMBERS(a) +#define FUNCTION_TRACE(a) +#define FUNCTION_TRACE_PTR(a,b) +#define FUNCTION_TRACE_U32(a,b) +#define FUNCTION_TRACE_STR(a,b) +#define FUNCTION_EXIT +#define FUNCTION_STATUS_EXIT(s) +#define FUNCTION_VALUE_EXIT(s) +#define DUMP_STACK_ENTRY(a) +#define DUMP_OPERANDS(a,b,c,d,e) +#define DUMP_ENTRY(a,b) +#define DUMP_TABLES(a,b) +#define DUMP_PATHNAME(a,b,c,d) +#define DEBUG_PRINT(l,f) +#define DEBUG_PRINT_RAW(l,f) +#define BREAK_MSG(a) + +#define return_VOID return +#define return_ACPI_STATUS(s) return(s) +#define return_VALUE(s) return(s) +#define return_PTR(s) return(s) + +#define ACPI_ASSERT(exp) +#define DEBUG_ASSERT(msg, exp) + +#endif + + +/* + * For 16-bit code, we want to shrink some things even though + * we are using ACPI_DEBUG to get the debug output + */ +#ifdef _IA16 +#undef DEBUG_ONLY_MEMBERS +#define DEBUG_ONLY_MEMBERS(a) +#undef OP_INFO_ENTRY +#define OP_INFO_ENTRY(opcode,flags,name,Pargs,Iargs) {opcode,flags,Pargs,Iargs} +#endif + + +#ifndef ACPI_DEBUG + +#define ADD_OBJECT_NAME(a,b) + +#else + + +/* + * 1) Set name to blanks + * 2) Copy the object name + */ + +#define ADD_OBJECT_NAME(a,b) MEMSET (a->common.name, ' ', sizeof (a->common.name));\ + STRNCPY (a->common.name, acpi_gbl_ns_type_names[b], sizeof (a->common.name)) + +#endif + + +#endif /* MACROS_H */ diff --git a/drivers/acpi/include/namesp.h b/drivers/acpi/include/namesp.h new file mode 100644 index 000000000..378b8a8c8 --- /dev/null +++ b/drivers/acpi/include/namesp.h @@ -0,0 +1,424 @@ + +/****************************************************************************** + * + * Name: namesp.h - Namespace subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __NAMESPACE_H__ +#define __NAMESPACE_H__ + +#include "actables.h" + + +/* To search the entire name space, pass this as Search_base */ + +#define NS_ALL ((ACPI_HANDLE)0) + +/* + * Elements of Acpi_ns_properties are bit significant + * and should be one-to-one with values of ACPI_OBJECT_TYPE + */ +#define NSP_NORMAL 0 +#define NSP_NEWSCOPE 1 /* a definition of this type opens a name scope */ +#define NSP_LOCAL 2 /* suppress search of enclosing scopes */ + + +/* Definitions of the predefined namespace names */ + +#define ACPI_UNKNOWN_NAME (u32) 0x3F3F3F3F /* Unknown name is "????" */ +#define ACPI_ROOT_NAME (u32) 0x2F202020 /* Root name is "/ " */ +#define ACPI_SYS_BUS_NAME (u32) 0x5F53425F /* Sys bus name is "_SB_" */ + +#define NS_ROOT_PATH "/" +#define NS_SYSTEM_BUS "_SB_" + + +/* Flags for Acpi_ns_lookup, Acpi_ns_search_and_enter */ + +#define NS_NO_UPSEARCH 0 +#define NS_SEARCH_PARENT 0x01 +#define NS_DONT_OPEN_SCOPE 0x02 +#define NS_NO_PEER_SEARCH 0x04 + +#define NS_WALK_UNLOCK TRUE +#define NS_WALK_NO_UNLOCK FALSE + + +ACPI_STATUS +acpi_ns_walk_namespace ( + OBJECT_TYPE_INTERNAL type, + ACPI_HANDLE start_object, + u32 max_depth, + u8 unlock_before_callback, + WALK_CALLBACK user_function, + void *context, + void **return_value); + + +ACPI_NAMED_OBJECT* +acpi_ns_get_next_object ( + OBJECT_TYPE_INTERNAL type, + ACPI_NAMED_OBJECT *parent, + ACPI_NAMED_OBJECT *child); + + +ACPI_STATUS +acpi_ns_delete_namespace_by_owner ( + u16 table_id); + +void +acpi_ns_free_table_entry ( + ACPI_NAMED_OBJECT *entry); + + +/* Namespace loading - nsload */ + +ACPI_STATUS +acpi_ns_parse_table ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAME_TABLE *scope); + +ACPI_STATUS +acpi_ns_load_table ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAMED_OBJECT *entry); + +ACPI_STATUS +acpi_ns_load_table_by_type ( + ACPI_TABLE_TYPE table_type); + + +/* + * Top-level namespace access - nsaccess + */ + + +ACPI_STATUS +acpi_ns_root_initialize ( + void); + +ACPI_STATUS +acpi_ns_lookup ( + ACPI_GENERIC_STATE *scope_info, + char *name, + OBJECT_TYPE_INTERNAL type, + OPERATING_MODE interpreter_mode, + u32 flags, + ACPI_WALK_STATE *walk_state, + ACPI_NAMED_OBJECT **ret_entry); + + +/* + * Table allocation/deallocation - nsalloc + */ + +ACPI_NAME_TABLE * +acpi_ns_allocate_name_table ( + u32 num_entries); + +ACPI_STATUS +acpi_ns_delete_namespace_subtree ( + ACPI_NAMED_OBJECT *parent_handle); + +void +acpi_ns_detach_object ( + ACPI_HANDLE object); + +void +acpi_ns_delete_name_table ( + ACPI_NAME_TABLE *name_table); + + +/* + * Namespace modification - nsmodify + */ + +ACPI_STATUS +acpi_ns_unload_namespace ( + ACPI_HANDLE handle); + +ACPI_STATUS +acpi_ns_delete_subtree ( + ACPI_HANDLE start_handle); + + +/* + * Namespace dump/print utilities - nsdump + */ + +void +acpi_ns_dump_tables ( + ACPI_HANDLE search_base, + s32 max_depth); + +void +acpi_ns_dump_entry ( + ACPI_HANDLE handle, + u32 debug_level); + +ACPI_STATUS +acpi_ns_dump_pathname ( + ACPI_HANDLE handle, + char *msg, + u32 level, + u32 component); + +void +acpi_ns_dump_root_devices ( + void); + +void +acpi_ns_dump_objects ( + OBJECT_TYPE_INTERNAL type, + u32 max_depth, + u32 ownder_id, + ACPI_HANDLE start_handle); + + +/* + * Namespace evaluation functions - nseval + */ + +ACPI_STATUS +acpi_ns_evaluate_by_handle ( + ACPI_NAMED_OBJECT *object_nte, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object); + +ACPI_STATUS +acpi_ns_evaluate_by_name ( + char *pathname, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object); + +ACPI_STATUS +acpi_ns_evaluate_relative ( + ACPI_NAMED_OBJECT *object_nte, + char *pathname, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object); + +ACPI_STATUS +acpi_ns_execute_control_method ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc); + +ACPI_STATUS +acpi_ns_get_object_value ( + ACPI_NAMED_OBJECT *object_entry, + ACPI_OBJECT_INTERNAL **return_obj_desc); + + +/* + * Parent/Child/Peer utility functions - nsfamily + */ + +ACPI_NAME +acpi_ns_find_parent_name ( + ACPI_NAMED_OBJECT *entry_to_search); + +u8 +acpi_ns_exist_downstream_sibling ( + ACPI_NAMED_OBJECT *this_entry); + + +/* + * Scope manipulation - nsscope + */ + +s32 +acpi_ns_opens_scope ( + OBJECT_TYPE_INTERNAL type); + +char * +acpi_ns_name_of_scope ( + ACPI_NAME_TABLE *scope); + +char * +acpi_ns_name_of_current_scope ( + ACPI_WALK_STATE *walk_state); + +ACPI_STATUS +acpi_ns_handle_to_pathname ( + ACPI_HANDLE obj_handle, + u32 *buf_size, + char *user_buffer); + +u8 +acpi_ns_pattern_match ( + ACPI_NAMED_OBJECT *obj_entry, + char *search_for); + +ACPI_STATUS +acpi_ns_name_compare ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value); + +void +acpi_ns_low_find_names ( + ACPI_NAMED_OBJECT *this_entry, + char *search_for, + s32 *count, + ACPI_HANDLE list[], + s32 max_depth); + +ACPI_HANDLE * +acpi_ns_find_names ( + char *search_for, + ACPI_HANDLE search_base, + s32 max_depth); + +ACPI_STATUS +acpi_ns_get_named_object ( + char *pathname, + ACPI_NAME_TABLE *in_scope, + ACPI_NAMED_OBJECT **out_nte); + +/* + * Object management for NTEs - nsobject + */ + +ACPI_STATUS +acpi_ns_attach_method ( + ACPI_HANDLE obj_handle, + u8 *pcode_addr, + u32 pcode_length); + +ACPI_STATUS +acpi_ns_attach_object ( + ACPI_HANDLE obj_handle, + ACPI_HANDLE value, + OBJECT_TYPE_INTERNAL type); + + +void * +acpi_ns_compare_value ( + ACPI_HANDLE obj_handle, + u32 level, + void *obj_desc); + +ACPI_HANDLE +acpi_ns_find_attached_object ( + ACPI_OBJECT_INTERNAL *obj_desc, + ACPI_HANDLE search_base, + s32 max_depth); + + +/* + * Namespace searching and entry - nssearch + */ + +ACPI_STATUS +acpi_ns_search_and_enter ( + u32 entry_name, + ACPI_WALK_STATE *walk_state, + ACPI_NAME_TABLE *name_table, + OPERATING_MODE interpreter_mode, + OBJECT_TYPE_INTERNAL type, + u32 flags, + ACPI_NAMED_OBJECT **ret_entry); + +void +acpi_ns_initialize_table ( + ACPI_NAME_TABLE *new_table, + ACPI_NAME_TABLE *parent_scope, + ACPI_NAMED_OBJECT *parent_entry); + +ACPI_STATUS +acpi_ns_search_one_scope ( + u32 entry_name, + ACPI_NAME_TABLE *name_table, + OBJECT_TYPE_INTERNAL type, + ACPI_NAMED_OBJECT **ret_entry, + NS_SEARCH_DATA *ret_info); + + +/* + * Utility functions - nsutils + */ + +u8 +acpi_ns_valid_root_prefix ( + char prefix); + +u8 +acpi_ns_valid_path_separator ( + char sep); + +OBJECT_TYPE_INTERNAL +acpi_ns_get_type ( + ACPI_HANDLE obj_handle); + +void * +acpi_ns_get_attached_object ( + ACPI_HANDLE obj_handle); + +s32 +acpi_ns_local ( + OBJECT_TYPE_INTERNAL type); + +ACPI_STATUS +acpi_ns_internalize_name ( + char *dotted_name, + char **converted_name); + +ACPI_STATUS +acpi_ns_externalize_name ( + u32 internal_name_length, + char *internal_name, + u32 *converted_name_length, + char **converted_name); + +s32 +is_ns_object ( + ACPI_OBJECT_INTERNAL *p_oD); + +s32 +acpi_ns_mark_nS( + void); + +ACPI_NAMED_OBJECT* +acpi_ns_convert_handle_to_entry ( + ACPI_HANDLE handle); + +ACPI_HANDLE +acpi_ns_convert_entry_to_handle( + ACPI_NAMED_OBJECT*nte); + +void +acpi_ns_terminate ( + void); + +ACPI_NAMED_OBJECT * +acpi_ns_get_parent_entry ( + ACPI_NAMED_OBJECT *this_entry); + + +ACPI_NAMED_OBJECT * +acpi_ns_get_next_valid_entry ( + ACPI_NAMED_OBJECT *this_entry); + + +#endif /* __NAMESPACE_H__ */ diff --git a/drivers/acpi/include/output.h b/drivers/acpi/include/output.h new file mode 100644 index 000000000..a3be12a38 --- /dev/null +++ b/drivers/acpi/include/output.h @@ -0,0 +1,124 @@ + +/****************************************************************************** + * + * Name: output.h -- debug output + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _OUTPUT_H +#define _OUTPUT_H + +/* + * Debug levels and component IDs. These are used to control the + * granularity of the output of the DEBUG_PRINT macro -- on a per- + * component basis and a per-exception-type basis. + */ + +/* Component IDs -- used in the global "Debug_layer" */ + +#define GLOBAL 0x00000001 +#define COMMON 0x00000002 +#define PARSER 0x00000004 +#define DISPATCHER 0x00000008 +#define INTERPRETER 0x00000010 +#define NAMESPACE 0x00000020 +#define RESOURCE_MANAGER 0x00000040 +#define TABLE_MANAGER 0x00000080 +#define EVENT_HANDLING 0x00000100 +#define HARDWARE 0x00000200 +#define MISCELLANEOUS 0x00000400 +#define OS_DEPENDENT 0x00000800 + +#define BUS_MANAGER 0x00001000 + +#define PROCESSOR_CONTROL 0x00002000 +#define SYSTEM_CONTROL 0x00004000 +#define THERMAL_CONTROL 0x00008000 +#define POWER_CONTROL 0x00010000 + +#define EMBEDDED_CONTROLLER 0x00020000 +#define BATTERY 0x00040000 + +#define DEBUGGER 0x00100000 +#define ALL_COMPONENTS 0x001FFFFF + + +/* Exception level -- used in the global "Debug_level" */ + +#define ACPI_OK 0x00000001 +#define ACPI_INFO 0x00000002 +#define ACPI_WARN 0x00000004 +#define ACPI_ERROR 0x00000008 +#define ACPI_FATAL 0x00000010 +#define ACPI_DEBUG_OBJECT 0x00000020 +#define ACPI_ALL 0x0000003F + + +/* Trace level -- also used in the global "Debug_level" */ + +#define TRACE_PARSE 0x00000100 +#define TRACE_DISPATCH 0x00000200 +#define TRACE_LOAD 0x00000400 +#define TRACE_EXEC 0x00000800 +#define TRACE_NAMES 0x00001000 +#define TRACE_OPREGION 0x00002000 +#define TRACE_BFIELD 0x00004000 +#define TRACE_TRASH 0x00008000 +#define TRACE_TABLES 0x00010000 +#define TRACE_FUNCTIONS 0x00020000 +#define TRACE_VALUES 0x00040000 +#define TRACE_OBJECTS 0x00080000 +#define TRACE_ALLOCATIONS 0x00100000 +#define TRACE_RESOURCES 0x00200000 +#define TRACE_IO 0x00400000 +#define TRACE_INTERRUPTS 0x00800000 +#define TRACE_USER_REQUESTS 0x01000000 +#define TRACE_PACKAGE 0x02000000 +#define TRACE_MUTEX 0x04000000 + +#define TRACE_ALL 0x0FFFFF00 + + +/* Exceptionally verbose output -- also used in the global "Debug_level" */ + +#define VERBOSE_AML_DISASSEMBLE 0x10000000 +#define VERBOSE_INFO 0x20000000 +#define VERBOSE_TABLES 0x40000000 +#define VERBOSE_EVENTS 0x80000000 + +#define VERBOSE_ALL 0x70000000 + + +/* Defaults for Debug_level, debug and normal */ + +#define DEBUG_DEFAULT (ACPI_OK | ACPI_WARN | ACPI_ERROR | ACPI_DEBUG_OBJECT | TRACE_TABLES | TRACE_IO) +#define NORMAL_DEFAULT (ACPI_OK | ACPI_WARN | ACPI_ERROR | ACPI_DEBUG_OBJECT) +#define DEBUG_ALL (VERBOSE_AML_DISASSEMBLE | TRACE_ALL | ACPI_ALL) + +/* Misc defines */ + +#define HEX 0x01 +#define ASCII 0x02 +#define FULL_ADDRESS 0x04 +#define CHARS_PER_LINE 16 /* used in Dump_buf function */ + + +#endif /* _OUTPUT_H */ diff --git a/drivers/acpi/include/parser.h b/drivers/acpi/include/parser.h new file mode 100644 index 000000000..ed146756a --- /dev/null +++ b/drivers/acpi/include/parser.h @@ -0,0 +1,327 @@ +/****************************************************************************** + * + * Module Name: parser.h - AML Parser subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PARSER_H_ +#define _PARSER_H_ + + +#define OP_HAS_RETURN_VALUE 1 + +/* variable # arguments */ + +#define ACPI_VAR_ARGS ACPI_UINT32_MAX + +/* maximum virtual address */ + +#define ACPI_MAX_AML ((u8 *)(~0UL)) + + +#define PARSE_DELETE_TREE 1 + + +/* psapi - Parser external interfaces */ + +ACPI_STATUS +acpi_psx_load_table ( + u8 *pcode_addr, + s32 pcode_length); + +ACPI_STATUS +acpi_psx_execute ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc); + + +u8 +acpi_ps_is_namespace_object_op ( + u16 opcode); +u8 +acpi_ps_is_namespace_op ( + u16 opcode); + + +/****************************************************************************** + * + * Parser interfaces + * + *****************************************************************************/ + + +/* psargs - Parse AML opcode arguments */ + +u8 * +acpi_ps_get_next_package_end ( + ACPI_PARSE_STATE *parser_state); + +char * +acpi_ps_get_next_namestring ( + ACPI_PARSE_STATE *parser_state); + +void +acpi_ps_get_next_simple_arg ( + ACPI_PARSE_STATE *parser_state, + s32 arg_type, /* type of argument */ + ACPI_GENERIC_OP *arg); /* (OUT) argument data */ + +void +acpi_ps_get_next_namepath ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *arg, + u32 *arg_count, + u8 method_call); + +ACPI_GENERIC_OP * +acpi_ps_get_next_field ( + ACPI_PARSE_STATE *parser_state); + +ACPI_GENERIC_OP * +acpi_ps_get_next_arg ( + ACPI_PARSE_STATE *parser_state, + s32 arg_type, + u32 *arg_count); + + +/* psopcode - AML Opcode information */ + +ACPI_OP_INFO * +acpi_ps_get_opcode_info ( + u16 opcode); + +char * +acpi_ps_get_opcode_name ( + u16 opcode); + + +/* psparse - top level parsing routines */ + +void +acpi_ps_delete_parse_tree ( + ACPI_GENERIC_OP *root); + +ACPI_STATUS +acpi_ps_parse_loop ( + ACPI_PARSE_STATE *parser_state, + ACPI_WALK_STATE *walk_state, + u32 parse_flags); + + +ACPI_STATUS +acpi_ps_parse_aml ( + ACPI_GENERIC_OP *start_scope, + u8 *aml, + u32 acpi_aml_size, + u32 parse_flags); + +ACPI_STATUS +acpi_ps_parse_table ( + u8 *aml, + s32 aml_size, + INTERPRETER_CALLBACK descending_callback, + INTERPRETER_CALLBACK ascending_callback, + ACPI_GENERIC_OP **root_object); + +u16 +acpi_ps_peek_opcode ( + ACPI_PARSE_STATE *state); + + +/* psscope - Scope stack management routines */ + + +ACPI_STATUS +acpi_ps_init_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *root); + +ACPI_GENERIC_OP * +acpi_ps_get_parent_scope ( + ACPI_PARSE_STATE *state); + +u8 +acpi_ps_has_completed_scope ( + ACPI_PARSE_STATE *parser_state); + +void +acpi_ps_pop_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP **op, + u32 *arg_list); + +ACPI_STATUS +acpi_ps_push_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *op, + u32 remaining_args, + u32 arg_count); + +void +acpi_ps_cleanup_scope ( + ACPI_PARSE_STATE *state); + + +/* pstree - parse tree manipulation routines */ + +void +acpi_ps_append_arg( + ACPI_GENERIC_OP *op, + ACPI_GENERIC_OP *arg); + +ACPI_GENERIC_OP* +acpi_ps_find ( + ACPI_GENERIC_OP *scope, + char *path, + u16 opcode, + u32 create); + +ACPI_GENERIC_OP * +acpi_ps_get_arg( + ACPI_GENERIC_OP *op, + u32 argn); + +ACPI_GENERIC_OP * +acpi_ps_get_child ( + ACPI_GENERIC_OP *op); + +ACPI_GENERIC_OP * +acpi_ps_get_depth_next ( + ACPI_GENERIC_OP *origin, + ACPI_GENERIC_OP *op); + + +/* pswalk - parse tree walk routines */ + +ACPI_STATUS +acpi_ps_walk_parsed_aml ( + ACPI_GENERIC_OP *start_op, + ACPI_GENERIC_OP *end_op, + ACPI_OBJECT_INTERNAL *mth_desc, + ACPI_NAME_TABLE *start_scope, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **caller_return_desc, + ACPI_OWNER_ID owner_id, + INTERPRETER_CALLBACK descending_callback, + INTERPRETER_CALLBACK ascending_callback); + +ACPI_STATUS +acpi_ps_get_next_walk_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + INTERPRETER_CALLBACK ascending_callback); + + +/* psutils - parser utilities */ + +void +acpi_ps_init_op ( + ACPI_GENERIC_OP *op, + u16 opcode); + +ACPI_GENERIC_OP * +acpi_ps_alloc_op ( + u16 opcode); + +void +acpi_ps_free_op ( + ACPI_GENERIC_OP *op); + +void +acpi_ps_delete_parse_cache ( + void); + +u8 +acpi_ps_is_leading_char ( + s32 c); + +u8 +acpi_ps_is_prefix_char ( + s32 c); + +u8 +acpi_ps_is_named_op ( + u16 opcode); + +u8 +acpi_ps_is_named_object_op ( + u16 opcode); + +u8 +acpi_ps_is_deferred_op ( + u16 opcode); + +u8 +acpi_ps_is_bytelist_op( + u16 opcode); + +u8 +acpi_ps_is_field_op( + u16 opcode); + +u8 +acpi_ps_is_create_field_op ( + u16 opcode); + +ACPI_NAMED_OP* +acpi_ps_to_named_op( + ACPI_GENERIC_OP *op); + +ACPI_DEFERRED_OP * +acpi_ps_to_deferred_op ( + ACPI_GENERIC_OP *op); + +ACPI_BYTELIST_OP* +acpi_ps_to_bytelist_op( + ACPI_GENERIC_OP *op); + +u32 +acpi_ps_get_name( + ACPI_GENERIC_OP *op); + +void +acpi_ps_set_name( + ACPI_GENERIC_OP *op, + u32 name); + + +/* psdump - display parser tree */ + +s32 +acpi_ps_sprint_path ( + char *buffer_start, + u32 buffer_size, + ACPI_GENERIC_OP *op); + +s32 +acpi_ps_sprint_op ( + char *buffer_start, + u32 buffer_size, + ACPI_GENERIC_OP *op); + +void +acpi_ps_show ( + ACPI_GENERIC_OP *op); + + +#endif /* _PARSER_H_ */ diff --git a/drivers/acpi/include/resource.h b/drivers/acpi/include/resource.h new file mode 100644 index 000000000..b87032ddb --- /dev/null +++ b/drivers/acpi/include/resource.h @@ -0,0 +1,300 @@ +/****************************************************************************** + * + * Name: resource.h - Resource Manager function prototypes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __RESOURCE_H__ +#define __RESOURCE_H__ + +#include "actypes.h" +#include "acobject.h" + +/* + * Function prototypes called from Acpi* APIs + */ + +ACPI_STATUS +acpi_rs_get_prt_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer); + + +ACPI_STATUS +acpi_rs_get_crs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_rs_get_prs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_rs_set_srs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer); + +ACPI_STATUS +acpi_rs_create_resource_list ( + ACPI_OBJECT_INTERNAL *byte_stream_buffer, + u8 *output_buffer, + u32 *output_buffer_length); + +ACPI_STATUS +acpi_rs_create_byte_stream ( + RESOURCE *linked_list_buffer, + u8 *output_buffer, + u32 *output_buffer_length); + +ACPI_STATUS +acpi_rs_create_pci_routing_table ( + ACPI_OBJECT_INTERNAL *method_return_object, + u8 *output_buffer, + u32 *output_buffer_length); + + +/* + *Function prototypes called from Acpi_rs_create*APIs + */ + +void +acpi_rs_dump_resource_list ( + RESOURCE *resource); + +void +acpi_rs_dump_irq_list ( + u8 *route_table); + +ACPI_STATUS +acpi_rs_get_byte_stream_start ( + u8 *byte_stream_buffer, + u8 **byte_stream_start, + u32 *size); + +ACPI_STATUS +acpi_rs_calculate_list_length ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + u32 *size_needed); + +ACPI_STATUS +acpi_rs_calculate_byte_stream_length ( + RESOURCE *linked_list_buffer, + u32 *size_needed); + +ACPI_STATUS +acpi_rs_byte_stream_to_list ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + u8 **output_buffer); + +ACPI_STATUS +acpi_rs_list_to_byte_stream ( + RESOURCE *linked_list, + u32 byte_stream_size_needed, + u8 **output_buffer); + +ACPI_STATUS +acpi_rs_io_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_fixed_io_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_io_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_fixed_io_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_irq_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_irq_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_dma_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_dma_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_address16_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_address16_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_address32_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_address32_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_start_dependent_functions_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_end_dependent_functions_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_start_dependent_functions_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_end_dependent_functions_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_memory24_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_memory24_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_memory32_range_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size +); + +ACPI_STATUS +acpi_rs_fixed_memory32_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_memory32_range_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_fixed_memory32_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_extended_irq_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_extended_irq_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_end_tag_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_end_tag_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + +ACPI_STATUS +acpi_rs_vendor_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size); + +ACPI_STATUS +acpi_rs_vendor_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed); + + +#endif /*__RESOURCE_H__ */ diff --git a/drivers/acpi/include/tables.h b/drivers/acpi/include/tables.h new file mode 100644 index 000000000..d3566489e --- /dev/null +++ b/drivers/acpi/include/tables.h @@ -0,0 +1,168 @@ + +/****************************************************************************** + * + * Name: tables.h - ACPI table management + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __TABLES_H__ +#define __TABLES_H__ + +#include "actypes.h" +#include "actables.h" + + +/* Used in Acpi_tb_map_acpi_table for size parameter if table header is to be used */ + +#define SIZE_IN_HEADER 0 + + +ACPI_STATUS +acpi_tb_handle_to_object ( + u16 table_id, + ACPI_TABLE_DESC **table_desc); + + +/* + * Acpi_tbfac - FACP, FACS utilities + */ + +ACPI_STATUS +acpi_tb_get_table_facs ( + char *buffer_ptr, + ACPI_TABLE_DESC *table_info); + + +/* + * Acpi_tbget - Table "get" routines + */ + +ACPI_STATUS +acpi_tb_get_table_ptr ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_TABLE_HEADER **table_ptr_loc); + +ACPI_STATUS +acpi_tb_get_table ( + void *physical_address, + char *buffer_ptr, + ACPI_TABLE_DESC *table_info); + + +/* + * Acpi_tbgetall - Get all firmware ACPI tables + */ + +ACPI_STATUS +acpi_tb_get_all_tables ( + u32 number_of_tables, + char *buffer_ptr); + + +/* + * Acpi_tbinstall - Table installation + */ + +ACPI_STATUS +acpi_tb_install_table ( + char *table_ptr, + ACPI_TABLE_DESC *table_info); + +ACPI_STATUS +acpi_tb_recognize_table ( + char *table_ptr, + ACPI_TABLE_DESC *table_info); + +ACPI_STATUS +acpi_tb_init_table_descriptor ( + ACPI_TABLE_TYPE table_type, + ACPI_TABLE_DESC *table_info); + + +/* + * Acpi_tbremove - Table removal and deletion + */ + +void +acpi_tb_delete_acpi_tables ( + void); + +void +acpi_tb_delete_acpi_table ( + ACPI_TABLE_TYPE type); + +ACPI_TABLE_DESC * +acpi_tb_delete_single_table ( + ACPI_TABLE_DESC *table_desc); + +void +acpi_tb_free_acpi_tables_of_type ( + ACPI_TABLE_DESC *table_info); + + +/* + * Acpi_tbrsd - RSDP, RSDT utilities + */ + +ACPI_STATUS +acpi_tb_get_table_rsdt ( + u32 *number_of_tables); + +char * +acpi_tb_scan_memory_for_rsdp ( + char *start_address, + u32 length); + +ACPI_STATUS +acpi_tb_find_rsdp ( + ACPI_TABLE_DESC *table_info); + + +/* + * Acpi_tbutils - common table utilities + */ + +u8 +acpi_tb_system_table_pointer ( + void *where); + +ACPI_STATUS +acpi_tb_map_acpi_table ( + void *physical_address, + u32 *size, + void **logical_address); + +ACPI_STATUS +acpi_tb_verify_table_checksum ( + ACPI_TABLE_HEADER *table_header); + +u8 +acpi_tb_checksum ( + void *buffer, + u32 length); + +ACPI_STATUS +acpi_tb_validate_table_header ( + ACPI_TABLE_HEADER *table_header); + + +#endif /* __TABLES_H__ */ diff --git a/drivers/acpi/interpreter/amconfig.c b/drivers/acpi/interpreter/amconfig.c new file mode 100644 index 000000000..8d4e4f26b --- /dev/null +++ b/drivers/acpi/interpreter/amconfig.c @@ -0,0 +1,303 @@ + +/****************************************************************************** + * + * Module Name: amconfig - Namespace reconfiguration (Load/Unload opcodes) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "events.h" +#include "tables.h" +#include "dispatch.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amconfig"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_load_table + * + * PARAMETERS: Rgn_desc - Op region where the table will be obtained + * Ddb_handle - Where a handle to the table will be returned + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_load_table ( + ACPI_OBJECT_INTERNAL *rgn_desc, + ACPI_HANDLE *ddb_handle) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *table_desc = NULL; + char *table_ptr; + char *table_data_ptr; + ACPI_TABLE_HEADER table_header; + ACPI_TABLE_DESC table_info; + u32 i; + + + /* TBD: [Unhandled] Object can be either a field or an opregion */ + + + /* Get the table header */ + + table_header.length = 0; + for (i = 0; i < sizeof (ACPI_TABLE_HEADER); i++) { + status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_READ, + i, 8, (u32 *) ((char *) &table_header + i)); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + /* Allocate a buffer for the entire table */ + + table_ptr = acpi_cm_allocate (table_header.length); + if (!table_ptr) { + return (AE_NO_MEMORY); + } + + /* Copy the header to the buffer */ + + MEMCPY (table_ptr, &table_header, sizeof (ACPI_TABLE_HEADER)); + table_data_ptr = table_ptr + sizeof (ACPI_TABLE_HEADER); + + + /* Get the table from the op region */ + + for (i = 0; i < table_header.length; i++) { + status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_READ, + i, 8, (u32 *) (table_data_ptr + i)); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + } + + + /* Table must be either an SSDT or a PSDT */ + + if ((!STRNCMP (table_header.signature, + acpi_gbl_acpi_table_data[ACPI_TABLE_PSDT].signature, + acpi_gbl_acpi_table_data[ACPI_TABLE_PSDT].sig_length)) && + (!STRNCMP (table_header.signature, + acpi_gbl_acpi_table_data[ACPI_TABLE_SSDT].signature, + acpi_gbl_acpi_table_data[ACPI_TABLE_SSDT].sig_length))) + { + status = AE_BAD_SIGNATURE; + goto cleanup; + } + + /* Create an object to be the table handle */ + + table_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); + if (!table_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + + /* Install the new table into the local data structures */ + + table_info.pointer = (ACPI_TABLE_HEADER *) table_ptr; + table_info.length = table_header.length; + table_info.allocation = ACPI_MEM_ALLOCATED; + table_info.base_pointer = table_ptr; + + status = acpi_tb_install_table (NULL, &table_info); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Add the table to the namespace */ + + status = acpi_load_namespace (); + if (ACPI_FAILURE (status)) { + /* TBD: [Errors] Unload the table on failure ? */ + + goto cleanup; + } + + /* TBD: [Investigate] we need a pointer to the table desc */ + + /* Init the table handle */ + + table_desc->reference.op_code = AML_LOAD_OP; + table_desc->reference.object = table_info.installed_desc; + + *ddb_handle = table_desc; + + return (status); + + +cleanup: + + acpi_cm_free (table_desc); + acpi_cm_free (table_ptr); + return (status); + +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_unload_table + * + * PARAMETERS: Ddb_handle - Handle to a previously loaded table + * + * RETURN: Status + * + * DESCRIPTION: Unload an ACPI table + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_unload_table ( + ACPI_HANDLE ddb_handle) +{ + ACPI_STATUS status = AE_NOT_IMPLEMENTED; + ACPI_OBJECT_INTERNAL *table_desc = (ACPI_OBJECT_INTERNAL *) ddb_handle; + ACPI_TABLE_DESC *table_info; + + + /* Validate the handle */ + /* TBD: [Errors] Wasn't this done earlier? */ + + if ((!ddb_handle) || + (!VALID_DESCRIPTOR_TYPE (ddb_handle, ACPI_DESC_TYPE_INTERNAL)) || + (((ACPI_OBJECT_INTERNAL *)ddb_handle)->common.type != + INTERNAL_TYPE_REFERENCE)) + { + return (AE_BAD_PARAMETER); + } + + + /* Get the actual table descriptor from the Ddb_handle */ + + table_info = (ACPI_TABLE_DESC *) table_desc->reference.object; + + /* + * Delete the entire namespace under this table NTE + * (Offset contains the Table_id) + */ + + status = acpi_ns_delete_namespace_by_owner (table_info->table_id); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Delete the table itself */ + + acpi_tb_delete_single_table (table_info->installed_desc); + + /* Delete the table descriptor (Ddb_handle) */ + + acpi_cm_remove_reference (table_desc); + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_reconfiguration + * + * PARAMETERS: Opcode - The opcode to be executed + * Walk_state - Current state of the parse tree walk + * + * RETURN: Status + * + * DESCRIPTION: Reconfiguration opcodes such as LOAD and UNLOAD + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_reconfiguration ( + u16 opcode, + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *region_desc = NULL; + ACPI_HANDLE *ddb_handle; + + + /* Resolve the operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get the table handle, common for both opcodes */ + + status |= acpi_ds_obj_stack_pop_object ((ACPI_OBJECT_INTERNAL **) &ddb_handle, + walk_state); + + switch (opcode) + { + + case AML_LOAD_OP: + + /* Get the region or field descriptor */ + + status |= acpi_ds_obj_stack_pop_object (®ion_desc, walk_state); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 2); + goto cleanup2; + } + + status = acpi_aml_exec_load_table (region_desc, ddb_handle); + break; + + + case AML_UN_LOAD_OP: + + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 1); + goto cleanup1; + } + + status = acpi_aml_exec_unload_table (ddb_handle); + break; + + + default: + + status = AE_AML_BAD_OPCODE; + break; + } + + +cleanup2: + acpi_cm_remove_reference (region_desc); + +cleanup1: + return (status); +} + diff --git a/drivers/acpi/interpreter/amcreate.c b/drivers/acpi/interpreter/amcreate.c new file mode 100644 index 000000000..f5c95bc11 --- /dev/null +++ b/drivers/acpi/interpreter/amcreate.c @@ -0,0 +1,871 @@ + +/****************************************************************************** + * + * Module Name: amcreate - Named object creation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "events.h" +#include "dispatch.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amcreate"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_field + * + * PARAMETERS: Opcode - The opcode to be executed + * Operands - List of operands for the opcode + * + * RETURN: Status + * + * DESCRIPTION: Execute Create_field operators: Create_bit_field_op, + * Create_byte_field_op, Create_word_field_op, Create_dWord_field_op, + * Create_field_op (which define fields in buffers) + * + * ALLOCATION: Deletes Create_field_op's count operand descriptor + * + * + * ACPI SPECIFICATION REFERENCES: + * Def_create_bit_field := Create_bit_field_op Src_buf Bit_idx Name_string + * Def_create_byte_field := Create_byte_field_op Src_buf Byte_idx Name_string + * Def_create_dWord_field := Create_dWord_field_op Src_buf Byte_idx Name_string + * Def_create_field := Create_field_op Src_buf Bit_idx Num_bits Name_string + * Def_create_word_field := Create_word_field_op Src_buf Byte_idx Name_string + * Bit_index := Term_arg=>Integer + * Byte_index := Term_arg=>Integer + * Num_bits := Term_arg=>Integer + * Source_buff := Term_arg=>Buffer + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_field ( + u16 opcode, + ACPI_WALK_STATE *walk_state) +{ + ACPI_OBJECT_INTERNAL *res_desc = NULL; + ACPI_OBJECT_INTERNAL *cnt_desc = NULL; + ACPI_OBJECT_INTERNAL *off_desc = NULL; + ACPI_OBJECT_INTERNAL *src_desc = NULL; + ACPI_OBJECT_INTERNAL *field_desc; + ACPI_OBJECT_INTERNAL *obj_desc; + OBJECT_TYPE_INTERNAL res_type; + ACPI_STATUS status; + u32 num_operands = 3; + u32 offset; + u32 bit_offset; + u16 bit_count; + u8 type_found; + + + /* Resolve the operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + + /* Get the operands */ + + status |= acpi_ds_obj_stack_pop_object (&res_desc, walk_state); + if (AML_CREATE_FIELD_OP == opcode) { + num_operands = 4; + status |= acpi_ds_obj_stack_pop_object (&cnt_desc, walk_state); + } + + status |= acpi_ds_obj_stack_pop_object (&off_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&src_desc, walk_state); + + if (status != AE_OK) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 3); + goto cleanup; + } + + + offset = off_desc->number.value; + + + /* + * If Res_desc is a Name, it will be a direct name pointer after + * Acpi_aml_resolve_operands() + */ + + if (!VALID_DESCRIPTOR_TYPE (res_desc, ACPI_DESC_TYPE_NAMED)) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + + /* + * Setup the Bit offsets and counts, according to the opcode + */ + + switch (opcode) + { + + /* Def_create_bit_field */ + + case AML_BIT_FIELD_OP: + + /* Offset is in bits, Field is a bit */ + + bit_offset = offset; + bit_count = 1; + break; + + + /* Def_create_byte_field */ + + case AML_BYTE_FIELD_OP: + + /* Offset is in bytes, field is a byte */ + + bit_offset = 8 * offset; + bit_count = 8; + break; + + + /* Def_create_word_field */ + + case AML_WORD_FIELD_OP: + + /* Offset is in bytes, field is a word */ + + bit_offset = 8 * offset; + bit_count = 16; + break; + + + /* Def_create_dWord_field */ + + case AML_DWORD_FIELD_OP: + + /* Offset is in bytes, field is a dword */ + + bit_offset = 8 * offset; + bit_count = 32; + break; + + + /* Def_create_field */ + + case AML_CREATE_FIELD_OP: + + /* Offset is in bits, count is in bits */ + + bit_offset = offset; + bit_count = (u16) cnt_desc->number.value; + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + /* + * Setup field according to the object type + */ + + switch (src_desc->common.type) + { + + /* Source_buff := Term_arg=>Buffer */ + + case ACPI_TYPE_BUFFER: + + if (bit_offset + (u32) bit_count > + (8 * (u32) src_desc->buffer.length)) + { + status = AE_AML_BUFFER_LIMIT; + goto cleanup; + } + + + /* Allocate an object for the field */ + + field_desc = acpi_cm_create_internal_object (ACPI_TYPE_FIELD_UNIT); + if (!field_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Construct the field object */ + + field_desc->field_unit.access = (u8) ACCESS_ANY_ACC; + field_desc->field_unit.lock_rule = (u8) GLOCK_NEVER_LOCK; + field_desc->field_unit.update_rule = (u8) UPDATE_PRESERVE; + field_desc->field_unit.length = bit_count; + field_desc->field_unit.bit_offset = (u8) (bit_offset % 8); + field_desc->field_unit.offset = DIV_8 (bit_offset); + field_desc->field_unit.container = src_desc; + field_desc->field_unit.sequence = src_desc->buffer.sequence; + + /* An additional reference for Src_desc */ + + acpi_cm_add_reference (src_desc); + + break; + + + /* Improper object type */ + + default: + + type_found = src_desc->common.type; + + if ((type_found > (u8) INTERNAL_TYPE_REFERENCE) || + !acpi_cm_valid_object_type (type_found)) + + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + + if (AML_CREATE_FIELD_OP == opcode) { + /* Delete object descriptor unique to Create_field */ + + acpi_cm_remove_reference (cnt_desc); + cnt_desc = NULL; + } + + /* + * This operation is supposed to cause the destination Name to refer + * to the defined Field_unit -- it must not store the constructed + * Field_unit object (or its current value) in some location that the + * Name may already be pointing to. So, if the Name currently contains + * a reference which would cause Acpi_aml_exec_store() to perform an indirect + * store rather than setting the value of the Name itself, clobber that + * reference before calling Acpi_aml_exec_store(). + */ + + res_type = acpi_ns_get_type (res_desc); + + /* Type of Name's existing value */ + + switch (res_type) + { + + case ACPI_TYPE_FIELD_UNIT: + + case INTERNAL_TYPE_ALIAS: + case INTERNAL_TYPE_BANK_FIELD: + case INTERNAL_TYPE_DEF_FIELD: + case INTERNAL_TYPE_INDEX_FIELD: + + obj_desc = acpi_ns_get_attached_object (res_desc); + if (obj_desc) { + /* + * There is an existing object here; delete it and zero out the + * NTE + */ + + acpi_cm_remove_reference (obj_desc); + acpi_ns_attach_object (res_desc, NULL, ACPI_TYPE_ANY); + } + + /* Set the type to ANY (or the store below will fail) */ + + ((ACPI_NAMED_OBJECT*) res_desc)->type = ACPI_TYPE_ANY; + + break; + + + default: + + break; + } + + + /* Store constructed field descriptor in result location */ + + status = acpi_aml_exec_store (field_desc, res_desc); + + /* + * If the field descriptor was not physically stored (or if a failure + * above), we must delete it + */ + if (field_desc->common.reference_count <= 1) { + acpi_cm_remove_reference (field_desc); + } + + +cleanup: + + /* Always delete the operands */ + + acpi_cm_remove_reference (off_desc); + acpi_cm_remove_reference (src_desc); + + if (AML_CREATE_FIELD_OP == opcode) { + acpi_cm_remove_reference (cnt_desc); + } + + /* On failure, delete the result descriptor */ + + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (res_desc); /* Result descriptor */ + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_alias + * + * PARAMETERS: Operands - List of operands for the opcode + * + * RETURN: Status + * + * DESCRIPTION: Create a new named alias + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_alias ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_NAMED_OBJECT *src_entry; + ACPI_NAMED_OBJECT *alias_entry; + ACPI_STATUS status; + + + /* Get the source/alias operands (both NTEs) */ + + status = acpi_ds_obj_stack_pop_object ((ACPI_OBJECT_INTERNAL **) &src_entry, + walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Don't pop it, it gets popped later */ + + alias_entry = acpi_ds_obj_stack_get_value (0, walk_state); + + /* Add an additional reference to the object */ + + acpi_cm_add_reference (src_entry->object); + + /* + * Attach the original source NTE to the new Alias NTE. + */ + status = acpi_ns_attach_object (alias_entry, src_entry->object, + src_entry->type); + + + /* + * The new alias assumes the type of the source, but it points + * to the same object. The reference count of the object has two + * additional references to prevent deletion out from under either the + * source or the alias NTE + */ + + /* Since both operands are NTEs, we don't need to delete them */ + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_event + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a new event object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_event ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + + + BREAKPOINT3; + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_EVENT); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Create the actual OS semaphore */ + + /* TBD: [Investigate] should be created with 0 or 1 units? */ + + status = acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, 1, + &obj_desc->event.semaphore); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + goto cleanup; + } + + /* Attach object to the NTE */ + + status = acpi_ns_attach_object (acpi_ds_obj_stack_get_value (0, walk_state), + obj_desc, (u8) ACPI_TYPE_EVENT); + if (ACPI_FAILURE (status)) { + acpi_os_delete_semaphore (obj_desc->event.semaphore); + acpi_cm_remove_reference (obj_desc); + goto cleanup; + } + + +cleanup: + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_mutex + * + * PARAMETERS: Interpreter_mode - Current running mode (load1/Load2/Exec) + * Operands - List of operands for the opcode + * + * RETURN: Status + * + * DESCRIPTION: Create a new mutex object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_mutex ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *sync_desc; + ACPI_OBJECT_INTERNAL *obj_desc; + + + /* Get the operand */ + + status = acpi_ds_obj_stack_pop_object (&sync_desc, walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Attempt to allocate a new object */ + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_MUTEX); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Create the actual OS semaphore */ + + status = acpi_os_create_semaphore (1, 1, &obj_desc->mutex.semaphore); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + goto cleanup; + } + + obj_desc->mutex.sync_level = (u8) sync_desc->number.value; + + /* Obj_desc was on the stack top, and the name is below it */ + + status = acpi_ns_attach_object (acpi_ds_obj_stack_get_value (0, walk_state), + obj_desc, (u8) ACPI_TYPE_MUTEX); + if (ACPI_FAILURE (status)) { + acpi_os_delete_semaphore (obj_desc->mutex.semaphore); + acpi_cm_remove_reference (obj_desc); + goto cleanup; + } + + +cleanup: + + /* Always delete the operand */ + + acpi_cm_remove_reference (sync_desc); + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_region + * + * PARAMETERS: Aml_ptr - Pointer to the region declaration AML + * Aml_length - Max length of the declaration AML + * Operands - List of operands for the opcode + * Interpreter_mode - Load1/Load2/Execute + * + * RETURN: Status + * + * DESCRIPTION: Create a new operation region object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_region ( + u8 *aml_ptr, + u32 aml_length, + u32 region_space, + ACPI_WALK_STATE *walk_state) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc_region; + ACPI_HANDLE *entry; + + + if (region_space >= NUM_REGION_TYPES) { + /* TBD: [Errors] should this return an error, or should we just keep + * going? */ + + REPORT_WARNING ("Unable to decode the Region_space"); + } + + + /* Get the NTE from the object stack */ + + entry = acpi_ds_obj_stack_get_value (0, walk_state); + + + /* Create the region descriptor */ + + obj_desc_region = acpi_cm_create_internal_object (ACPI_TYPE_REGION); + if (!obj_desc_region) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Allocate a method object for this region. + */ + obj_desc_region->region.method = acpi_cm_create_internal_object ( + ACPI_TYPE_METHOD); + if (!obj_desc_region->region.method) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Init the region from the operands */ + + obj_desc_region->region.space_id = (u16) region_space; + obj_desc_region->region.address = 0; + obj_desc_region->region.length = 0; + obj_desc_region->region.region_flags = 0; + + /* + * Remember location in AML stream of address & length + * operands since they need to be evaluated at run time. + */ + obj_desc_region->region.method->method.pcode = aml_ptr; + obj_desc_region->region.method->method.pcode_length = aml_length; + + + /* Install the new region object in the parent NTE */ + + obj_desc_region->region.nte = (ACPI_NAMED_OBJECT*) entry; + + status = acpi_ns_attach_object (entry, obj_desc_region, + (u8) ACPI_TYPE_REGION); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + +cleanup: + + if (status != AE_OK) { + /* Delete region object and method subobject */ + + if (obj_desc_region) { + /* Remove deletes both objects! */ + + acpi_cm_remove_reference (obj_desc_region); + obj_desc_region = NULL; + } + } + + + /* + * If we have a valid region, initialize it + */ + if (obj_desc_region) { + /* + * TBD: [Errors] Is there anything we can or could do when this + * fails? + * We need to do something useful with a failure. + */ + /* Namespace IS locked */ + + (void *) acpi_ev_initialize_region (obj_desc_region, TRUE); + + } + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_processor + * + * PARAMETERS: Op - Op containing the Processor definition and + * args + * Processor_nTE - NTE for the containing NTE + * + * RETURN: Status + * + * DESCRIPTION: Create a new processor object and populate the fields + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_processor ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE processor_nTE) +{ + ACPI_STATUS status; + ACPI_GENERIC_OP *arg; + ACPI_OBJECT_INTERNAL *obj_desc; + + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_PROCESSOR); + if (!obj_desc) { + status = AE_NO_MEMORY; + return (status); + } + + /* Install the new processor object in the parent NTE */ + + status = acpi_ns_attach_object (processor_nTE, obj_desc, + (u8) ACPI_TYPE_PROCESSOR); + if (ACPI_FAILURE (status)) { + return(status); + } + + arg = op->value.arg; + + /* check existence */ + + if (!arg) { + status = AE_AML_NO_OPERAND; + return (status); + } + + /* First arg is the Processor ID */ + + obj_desc->processor.proc_id = (u8) arg->value.integer; + + /* Move to next arg and check existence */ + + arg = arg->next; + if (!arg) { + status = AE_AML_NO_OPERAND; + return (status); + } + + /* Second arg is the PBlock Address */ + + obj_desc->processor.pblk_address = (ACPI_IO_ADDRESS) arg->value.integer; + + /* Move to next arg and check existence */ + + arg = arg->next; + if (!arg) { + status = AE_AML_NO_OPERAND; + return (status); + } + + /* Third arg is the PBlock Length */ + + obj_desc->processor.pblk_length = (u8) arg->value.integer; + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_power_resource + * + * PARAMETERS: Op - Op containing the Power_resource definition + * and args + * Power_res_nTE - NTE for the containing NTE + * + * RETURN: Status + * + * DESCRIPTION: Create a new Power_resource object and populate the fields + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_power_resource ( + ACPI_GENERIC_OP *op, + ACPI_HANDLE power_res_nTE) +{ + ACPI_STATUS status; + ACPI_GENERIC_OP *arg; + ACPI_OBJECT_INTERNAL *obj_desc; + + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_POWER); + if (!obj_desc) { + status = AE_NO_MEMORY; + return (status); + } + + /* Install the new power resource object in the parent NTE */ + + status = acpi_ns_attach_object (power_res_nTE, obj_desc, + (u8) ACPI_TYPE_POWER); + if (ACPI_FAILURE (status)) { + return(status); + } + + arg = op->value.arg; + + /* check existence */ + + if (!arg) { + status = AE_AML_NO_OPERAND; + return (status); + } + + /* First arg is the System_level */ + + obj_desc->power_resource.system_level = (u8) arg->value.integer; + + /* Move to next arg and check existence */ + + arg = arg->next; + if (!arg) { + status = AE_AML_NO_OPERAND; + return (status); + } + + /* Second arg is the PBlock Address */ + + obj_desc->power_resource.resource_order = (u16) arg->value.integer; + + return (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_create_method + * + * PARAMETERS: Interpreter_mode - Current running mode (load1/Load2/Exec) + * + * RETURN: Status + * + * DESCRIPTION: Create a new mutex object + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_create_method ( + u8 *aml_ptr, + u32 aml_length, + u32 method_flags, + ACPI_HANDLE method) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Create a new method object */ + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_METHOD); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* Get the method's AML pointer/length from the Op */ + + obj_desc->method.pcode = aml_ptr; + obj_desc->method.pcode_length = aml_length; + + /* + * First argument is the Method Flags (contains parameter count for the + * method) + */ + + obj_desc->method.method_flags = (u8) method_flags; + obj_desc->method.param_count = (u8) (method_flags & + METHOD_FLAGS_ARG_COUNT); + + /* + * Get the concurrency count + * If required, a semaphore will be created for this method when it is + * parsed. + * + * TBD: [Future] for APCI 2.0, there will be a Sync_level value, not + * just a flag + * Concurrency = Sync_level + 1;. + */ + + if (method_flags & METHOD_FLAGS_SERIALIZED) { + obj_desc->method.concurrency = 1; + } + else { + obj_desc->method.concurrency = INFINITE_CONCURRENCY; + } + + /* Mark the Method as not parsed yet */ + + obj_desc->method.parser_op = NULL; + + /* + * Another +1 gets added when Acpi_psx_execute is called, + * no need for: Obj_desc->Method.Pcode++; + */ + + obj_desc->method.acpi_table = NULL; /* TBD: [Restructure] was (u8 *) Pcode_addr; */ + obj_desc->method.table_length = 0; /* TBD: [Restructure] needed? (u32) (Walk_state->aml_end - Pcode_addr); */ + + /* Attach the new object to the method NTE */ + + status = acpi_ns_attach_object (method, obj_desc, (u8) ACPI_TYPE_METHOD); + if (ACPI_FAILURE (status)) { + acpi_cm_free (obj_desc); + } + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amdyadic.c b/drivers/acpi/interpreter/amdyadic.c new file mode 100644 index 000000000..7c32edd0e --- /dev/null +++ b/drivers/acpi/interpreter/amdyadic.c @@ -0,0 +1,750 @@ + +/****************************************************************************** + * + * Module Name: amdyadic - ACPI AML (p-code) execution for dyadic operators + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "namesp.h" +#include "interp.h" +#include "events.h" +#include "amlcode.h" +#include "dispatch.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amdyadic"); + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_dyadic1 + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 1 dyadic operator with numeric operands: + * Notify_op + * + * ALLOCATION: Deletes both operands + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_dyadic1 ( + u16 opcode, + ACPI_WALK_STATE *walk_state) +{ + ACPI_OBJECT_INTERNAL *obj_desc = NULL; + ACPI_OBJECT_INTERNAL *val_desc = NULL; + ACPI_NAMED_OBJECT *entry; + ACPI_STATUS status = AE_OK; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get the operands */ + + status |= acpi_ds_obj_stack_pop_object (&val_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 2); + goto cleanup; + } + + + /* Examine the opcode */ + + switch (opcode) + { + + /* Def_notify := Notify_op Notify_object Notify_value */ + + case AML_NOTIFY_OP: + + /* The Obj_desc is actually an NTE */ + + entry = (ACPI_NAMED_OBJECT*) obj_desc; + obj_desc = NULL; + + /* Object must be a device or thermal zone */ + + if (entry && val_desc) { + switch (entry->type) + { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_THERMAL: + + /* + * Requires that Device and Thermal_zone be compatible + * mappings + */ + + /* Dispatch the notify to the appropriate handler */ + + acpi_ev_notify_dispatch (entry, val_desc->number.value); + break; + + default: + status = AE_AML_OPERAND_TYPE; + } + } + break; + + default: + status = AE_AML_BAD_OPCODE; + } + + +cleanup: + + /* Always delete both operands */ + + acpi_cm_remove_reference (val_desc); + acpi_cm_remove_reference (obj_desc); + + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_dyadic2_r + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 2 dyadic operator with numeric operands and + * one or two result operands. + * + * ALLOCATION: Deletes one operand descriptor -- other remains on stack + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_dyadic2_r ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc = NULL; + ACPI_OBJECT_INTERNAL *obj_desc2 = NULL; + ACPI_OBJECT_INTERNAL *res_desc = NULL; + ACPI_OBJECT_INTERNAL *res_desc2 = NULL; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_OBJECT_INTERNAL *ret_desc2 = NULL; + ACPI_STATUS status = AE_OK; + u32 remainder; + s32 num_operands = 3; + char *new_buf; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + if (AML_DIVIDE_OP == opcode) { + num_operands = 4; + status |= acpi_ds_obj_stack_pop_object (&res_desc2, walk_state); + } + + status |= acpi_ds_obj_stack_pop_object (&res_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc2, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + &(walk_state->operands [walk_state->num_operands -1]), + num_operands); + goto cleanup; + } + + + /* Create an internal return object if necessary */ + + switch (opcode) + { + case AML_ADD_OP: + case AML_BIT_AND_OP: + case AML_BIT_NAND_OP: + case AML_BIT_OR_OP: + case AML_BIT_NOR_OP: + case AML_BIT_XOR_OP: + case AML_DIVIDE_OP: + case AML_MULTIPLY_OP: + case AML_SHIFT_LEFT_OP: + case AML_SHIFT_RIGHT_OP: + case AML_SUBTRACT_OP: + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + break; + } + + + /* + * Execute the opcode + */ + + switch (opcode) + { + + /* Def_add := Add_op Operand1 Operand2 Result */ + + case AML_ADD_OP: + + ret_desc->number.value = obj_desc->number.value + + obj_desc2->number.value; + break; + + + /* Def_and := And_op Operand1 Operand2 Result */ + + case AML_BIT_AND_OP: + + ret_desc->number.value = obj_desc->number.value & + obj_desc2->number.value; + break; + + + /* Def_nAnd := NAnd_op Operand1 Operand2 Result */ + + case AML_BIT_NAND_OP: + + ret_desc->number.value = ~(obj_desc->number.value & + obj_desc2->number.value); + break; + + + /* Def_or := Or_op Operand1 Operand2 Result */ + + case AML_BIT_OR_OP: + + ret_desc->number.value = obj_desc->number.value | + obj_desc2->number.value; + break; + + + /* Def_nOr := NOr_op Operand1 Operand2 Result */ + + case AML_BIT_NOR_OP: + + ret_desc->number.value = ~(obj_desc->number.value | + obj_desc2->number.value); + break; + + + /* Def_xOr := XOr_op Operand1 Operand2 Result */ + + case AML_BIT_XOR_OP: + + ret_desc->number.value = obj_desc->number.value ^ + obj_desc2->number.value; + break; + + + /* Def_divide := Divide_op Dividend Divisor Remainder Quotient */ + + case AML_DIVIDE_OP: + + if ((u32) 0 == obj_desc2->number.value) { + REPORT_ERROR ("Aml_exec_dyadic2_r/Divide_op: Divide by zero"); + + status = AE_AML_DIVIDE_BY_ZERO; + goto cleanup; + } + + ret_desc2 = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc2) { + status = AE_NO_MEMORY; + goto cleanup; + } + + remainder = obj_desc->number.value % + obj_desc2->number.value; + ret_desc->number.value = remainder; + + /* Result (what we used to call the quotient) */ + + ret_desc2->number.value = obj_desc->number.value / + obj_desc2->number.value; + break; + + + /* Def_multiply := Multiply_op Operand1 Operand2 Result */ + + case AML_MULTIPLY_OP: + + ret_desc->number.value = obj_desc->number.value * + obj_desc2->number.value; + break; + + + /* Def_shift_left := Shift_left_op Operand Shift_count Result */ + + case AML_SHIFT_LEFT_OP: + + ret_desc->number.value = obj_desc->number.value << + obj_desc2->number.value; + break; + + + /* Def_shift_right := Shift_right_op Operand Shift_count Result */ + + case AML_SHIFT_RIGHT_OP: + + ret_desc->number.value = obj_desc->number.value >> + obj_desc2->number.value; + break; + + + /* Def_subtract := Subtract_op Operand1 Operand2 Result */ + + case AML_SUBTRACT_OP: + + ret_desc->number.value = obj_desc->number.value - + obj_desc2->number.value; + break; + + + /* Def_concat := Concat_op Data1 Data2 Result */ + + case AML_CONCAT_OP: + + if (obj_desc2->common.type != obj_desc->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* Both operands are now known to be the same */ + + if (ACPI_TYPE_STRING == obj_desc->common.type) { + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_STRING); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Operand1 is string */ + + new_buf = acpi_cm_allocate (obj_desc->string.length + + obj_desc2->string.length + 1); + if (!new_buf) { + REPORT_ERROR + ("Aml_exec_dyadic2_r/Concat_op: String allocation failure"); + status = AE_NO_MEMORY; + goto cleanup; + } + + STRCPY (new_buf, (char *) obj_desc->string.pointer); + STRCPY (new_buf + obj_desc->string.length, + (char *) obj_desc2->string.pointer); + + /* Point the return object to the new string */ + + ret_desc->string.pointer = new_buf; + ret_desc->string.length = obj_desc->string.length += + obj_desc2->string.length; + } + + else { + /* Operand1 is not a string ==> must be a buffer */ + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_BUFFER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = acpi_cm_allocate (obj_desc->buffer.length + + obj_desc2->buffer.length); + if (!new_buf) { + /* Only bail out if the buffer is small */ + + /* TBD: [Investigate] what is the point of this code? */ + + if (obj_desc->buffer.length + obj_desc2->buffer.length < 1024) { + REPORT_ERROR + ("Aml_exec_dyadic2_r/Concat_op: Buffer allocation failure"); + return (AE_NO_MEMORY); + } + + status = AE_NO_MEMORY; + goto cleanup; + } + + MEMCPY (new_buf, obj_desc->buffer.pointer, + obj_desc->buffer.length); + MEMCPY (new_buf + obj_desc->buffer.length, obj_desc2->buffer.pointer, + obj_desc2->buffer.length); + + /* + * Point the return object to the new buffer + */ + + ret_desc->buffer.pointer = (u8 *) new_buf; + ret_desc->buffer.length = obj_desc->buffer.length + + obj_desc2->buffer.length; + } + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + /* + * Store the result of the operation (which is now in Obj_desc) into + * the result descriptor, or the location pointed to by the result + * descriptor (Res_desc). + */ + + if ((status = acpi_aml_exec_store (ret_desc, res_desc)) != AE_OK) { + goto cleanup; + } + + if (AML_DIVIDE_OP == opcode) { + status = acpi_aml_exec_store (ret_desc2, res_desc2); + + /* + * Since the remainder is not returned, remove a reference to + * the object we created earlier + */ + + acpi_cm_remove_reference (ret_desc2); + } + + +cleanup: + + /* Always delete the operands */ + + acpi_cm_remove_reference (obj_desc); + acpi_cm_remove_reference (obj_desc2); + + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + /* On failure, delete the result ops */ + + acpi_cm_remove_reference (res_desc); + acpi_cm_remove_reference (res_desc2); + + if (ret_desc) { + /* And delete the internal return object */ + + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + } + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_dyadic2_s + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 2 dyadic synchronization operator + * + * ALLOCATION: Deletes one operand descriptor -- other remains on stack + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_dyadic2_s ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *time_desc; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_STATUS status; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&time_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 2); + goto cleanup; + } + + + /* Create the internal return object */ + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Default return value is FALSE, operation did not time out */ + + ret_desc->number.value = 0; + + + /* Examine the opcode */ + + switch (opcode) + { + + /* Def_acquire := Acquire_op Mutex_object Timeout */ + + case AML_ACQUIRE_OP: + + status = acpi_aml_system_acquire_mutex (time_desc, obj_desc); + break; + + + /* Def_wait := Wait_op Acpi_event_object Timeout */ + + case AML_WAIT_OP: + + status = acpi_aml_system_wait_event (time_desc, obj_desc); + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + /* + * Return a boolean indicating if operation timed out + * (TRUE) or not (FALSE) + */ + + if (status == AE_TIME) { + ret_desc->number.value = (u32)(-1); /* TRUE, op timed out */ + status = AE_OK; + } + + +cleanup: + + /* Delete params */ + + acpi_cm_remove_reference (time_desc); + acpi_cm_remove_reference (obj_desc); + + /* Delete return object on error */ + + if (ACPI_FAILURE (status) && + (ret_desc)) + { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_exec_dyadic2 + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 2 dyadic operator with numeric operands and + * no result operands + * + * ALLOCATION: Deletes one operand descriptor -- other remains on stack + * containing result value + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_dyadic2 ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *obj_desc2; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_STATUS status; + u8 lboolean; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&obj_desc2, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, opcode, + WALK_OPERANDS, 2); + goto cleanup; + } + + + /* Create the internal return object */ + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Execute the Opcode + */ + + lboolean = FALSE; + switch (opcode) + { + + /* Def_lAnd := LAnd_op Operand1 Operand2 */ + + case AML_LAND_OP: + + lboolean = (u8) (obj_desc->number.value && + obj_desc2->number.value); + break; + + + /* Def_lEqual := LEqual_op Operand1 Operand2 */ + + case AML_LEQUAL_OP: + + lboolean = (u8) (obj_desc->number.value == + obj_desc2->number.value); + break; + + + /* Def_lGreater := LGreater_op Operand1 Operand2 */ + + case AML_LGREATER_OP: + + lboolean = (u8) (obj_desc->number.value > + obj_desc2->number.value); + break; + + + /* Def_lLess := LLess_op Operand1 Operand2 */ + + case AML_LLESS_OP: + + lboolean = (u8) (obj_desc->number.value < + obj_desc2->number.value); + break; + + + /* Def_lOr := LOr_op Operand1 Operand2 */ + + case AML_LOR_OP: + + lboolean = (u8) (obj_desc->number.value || + obj_desc2->number.value); + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + break; + } + + + /* Set return value to logical TRUE (all ones) or FALSE (zero) */ + + if (lboolean) { + ret_desc->number.value = 0xffffffff; + } + else { + ret_desc->number.value = 0; + } + + +cleanup: + + /* Always delete operands */ + + acpi_cm_remove_reference (obj_desc); + acpi_cm_remove_reference (obj_desc2); + + + /* Delete return object on error */ + + if (ACPI_FAILURE (status) && + (ret_desc)) + { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} + + diff --git a/drivers/acpi/interpreter/amfield.c b/drivers/acpi/interpreter/amfield.c new file mode 100644 index 000000000..c77593854 --- /dev/null +++ b/drivers/acpi/interpreter/amfield.c @@ -0,0 +1,327 @@ +/****************************************************************************** + * + * Module Name: amfield - ACPI AML (p-code) execution - field manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "hardware.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amfield"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_setup_field + * + * PARAMETERS: *Obj_desc - Field to be read or written + * *Rgn_desc - Region containing field + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Common processing for Acpi_aml_read_field and Acpi_aml_write_field + * + * ACPI SPECIFICATION REFERENCES: + * Each of the Type1_opcodes is defined as specified in in-line + * comments below. For each one, use the following definitions. + * + * Def_bit_field := Bit_field_op Src_buf Bit_idx Destination + * Def_byte_field := Byte_field_op Src_buf Byte_idx Destination + * Def_create_field := Create_field_op Src_buf Bit_idx Num_bits Name_string + * Def_dWord_field := DWord_field_op Src_buf Byte_idx Destination + * Def_word_field := Word_field_op Src_buf Byte_idx Destination + * Bit_index := Term_arg=>Integer + * Byte_index := Term_arg=>Integer + * Destination := Name_string + * Num_bits := Term_arg=>Integer + * Source_buf := Term_arg=>Buffer + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_setup_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + ACPI_OBJECT_INTERNAL *rgn_desc, + s32 field_bit_width) +{ + ACPI_STATUS status = AE_OK; + s32 field_byte_width; + + + /* Parameter validation */ + + if (!obj_desc || !rgn_desc) { + return (AE_AML_NO_OPERAND); + } + + if (ACPI_TYPE_REGION != rgn_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + + /* + * Init and validate Field width + * Possible values are 1, 2, 4 + */ + + field_byte_width = DIV_8 (field_bit_width); + + if ((field_bit_width != 8) && + (field_bit_width != 16) && + (field_bit_width != 32)) + { + return (AE_AML_OPERAND_VALUE); + } + + + /* + * If the address and length have not been previously evaluated, + * evaluate them and save the results. + */ + if (!(rgn_desc->region.region_flags & REGION_AGRUMENT_DATA_VALID)) { + + status = acpi_ds_get_region_arguments (rgn_desc); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + + /* + * If (offset rounded up to next multiple of field width) + * exceeds region length, indicate an error. + */ + + if (rgn_desc->region.length < + (obj_desc->field.offset & ~((u32) field_byte_width - 1)) + + field_byte_width) + { + /* + * Offset rounded up to next multiple of field width + * exceeds region length, indicate an error + */ + + return (AE_AML_REGION_LIMIT); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_access_named_field + * + * PARAMETERS: Mode - ACPI_READ or ACPI_WRITE + * Named_field - Handle for field to be accessed + * *Buffer - Value(s) to be read or written + * Buffer_length - Number of bytes to transfer + * + * RETURN: Status + * + * DESCRIPTION: Read or write a named field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_access_named_field ( + s32 mode, + ACPI_HANDLE named_field, + void *buffer, + u32 buffer_length) +{ + ACPI_OBJECT_INTERNAL *obj_desc = NULL; + ACPI_STATUS status = AE_OK; + u8 locked = FALSE; + u32 bit_granularity = 0; + u32 byte_granularity; + u32 datum_length; + u32 actual_byte_length; + u32 byte_field_length; + + + /* Get the attached field object */ + + obj_desc = acpi_ns_get_attached_object (named_field); + if (!obj_desc) { + return (AE_AML_INTERNAL); + } + + /* Check the type */ + + if (INTERNAL_TYPE_DEF_FIELD != acpi_ns_get_type (named_field)) { + return (AE_AML_OPERAND_TYPE); + } + + /* Obj_desc valid and Named_field is a defined field */ + + + /* Double-check that the attached object is also a field */ + + if (INTERNAL_TYPE_DEF_FIELD != obj_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + + /* + * Granularity was decoded from the field access type + * (Any_acc will be the same as Byte_acc) + */ + + bit_granularity = obj_desc->field_unit.granularity; + byte_granularity = DIV_8 (bit_granularity); + + /* + * Check if request is too large for the field, and silently truncate + * if necessary + */ + + /* TBD: [Errors] should an error be returned in this case? */ + + byte_field_length = (u32) DIV_8 (obj_desc->field_unit.length + 7); + + + actual_byte_length = buffer_length; + if (buffer_length > byte_field_length) { + actual_byte_length = byte_field_length; + + } + + + /* Convert byte count to datum count, round up if necessary */ + + datum_length = (actual_byte_length + (byte_granularity-1)) / byte_granularity; + + + /* Get the global lock if needed */ + + locked = acpi_aml_acquire_global_lock (obj_desc->field_unit.lock_rule); + + + /* Perform the actual read or write of the buffer */ + + switch (mode) + { + case ACPI_READ: + + status = acpi_aml_read_field (obj_desc, buffer, buffer_length, + actual_byte_length, datum_length, + bit_granularity, byte_granularity); + break; + + + case ACPI_WRITE: + + status = acpi_aml_write_field (obj_desc, buffer, buffer_length, + actual_byte_length, datum_length, + bit_granularity, byte_granularity); + break; + + + default: + + status = AE_BAD_PARAMETER; + break; + } + + + /* Release global lock if we acquired it earlier */ + + acpi_aml_release_global_lock (locked); + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_set_named_field_value + * + * PARAMETERS: Named_field - Handle for field to be set + * Buffer - Bytes to be stored + * Buffer_length - Number of bytes to be stored + * + * RETURN: Status + * + * DESCRIPTION: Store the given value into the field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_set_named_field_value ( + ACPI_HANDLE named_field, + void *buffer, + u32 buffer_length) +{ + ACPI_STATUS status; + + + if (!named_field) { + return (AE_AML_INTERNAL); + } + + status = acpi_aml_access_named_field (ACPI_WRITE, named_field, buffer, + buffer_length); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_get_named_field_value + * + * PARAMETERS: Named_field - Handle for field to be read + * *Buffer - Where to store value read from field + * Buffer_length - Max length to read + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value of the given field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_get_named_field_value ( + ACPI_HANDLE named_field, + void *buffer, + u32 buffer_length) +{ + ACPI_STATUS status; + + + if ((!named_field) || (!buffer)) { + return (AE_AML_INTERNAL); + } + + status = acpi_aml_access_named_field (ACPI_READ, named_field, buffer, + buffer_length); + return (status); +} + diff --git a/drivers/acpi/interpreter/amfldio.c b/drivers/acpi/interpreter/amfldio.c new file mode 100644 index 000000000..bde4d5f12 --- /dev/null +++ b/drivers/acpi/interpreter/amfldio.c @@ -0,0 +1,654 @@ +/****************************************************************************** + * + * Module Name: amfldio - Aml Field I/O + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "hardware.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amfldio"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_read_field_data + * + * PARAMETERS: *Obj_desc - Field to be read + * *Value - Where to store value + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value of the given field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_read_field_data ( + ACPI_OBJECT_INTERNAL *obj_desc, + u32 field_byte_offset, + u32 field_bit_width, + u32 *value) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *rgn_desc = NULL; + u32 address; + u32 local_value = 0; + s32 field_byte_width; + + + /* Obj_desc is validated by callers */ + + if (obj_desc) { + rgn_desc = obj_desc->field.container; + } + + + field_byte_width = DIV_8 (field_bit_width); + status = acpi_aml_setup_field (obj_desc, rgn_desc, field_bit_width); + if (AE_OK != status) { + return (status); + } + + /* Setup_field validated Rgn_desc and Field_bit_width */ + + if (!value) { + value = &local_value; /* support reads without saving value */ + } + + + /* + * Round offset down to next multiple of + * field width, add region base address and offset within the field + */ + + address = rgn_desc->region.address + + (obj_desc->field.offset & ~((u32) field_byte_width - 1)) + + field_byte_offset; + + + + + /* Invoke the appropriate Address_space/Op_region handler */ + + status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_READ, + address, field_bit_width, value); + + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_read_field + * + * PARAMETERS: *Obj_desc - Field to be read + * *Value - Where to store value + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value of the given field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_read_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + void *buffer, + u32 buffer_length, + u32 byte_length, + u32 datum_length, + u32 bit_granularity, + u32 byte_granularity) +{ + ACPI_STATUS status; + u32 this_field_byte_offset; + u32 this_field_datum_offset; + u32 previous_raw_datum; + u32 this_raw_datum; + u32 valid_field_bits; + u32 mask; + u32 merged_datum = 0; + + + /* + * Clear the caller's buffer (the whole buffer length as given) + * This is very important, especially in the cases where a byte is read, + * but the buffer is really a u32 (4 bytes). + */ + + MEMSET (buffer, 0, buffer_length); + + /* Read the first raw datum to prime the loop */ + + this_field_byte_offset = 0; + this_field_datum_offset= 0; + + status = acpi_aml_read_field_data (obj_desc, this_field_byte_offset, bit_granularity, + &previous_raw_datum); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* We might actually be done if the request fits in one datum */ + + if ((datum_length == 1) && + ((obj_desc->field.bit_offset + obj_desc->field_unit.length) <= + (u16) bit_granularity)) + { + merged_datum = previous_raw_datum; + + merged_datum = (merged_datum >> obj_desc->field.bit_offset); + + valid_field_bits = obj_desc->field_unit.length % bit_granularity; + if (valid_field_bits) { + mask = (((u32) 1 << valid_field_bits) - (u32) 1); + merged_datum &= mask; + } + + + /* + * Place the Merged_datum into the proper format and return buffer + * field + */ + + switch (byte_granularity) + { + case 1: + ((u8 *) buffer) [this_field_datum_offset] = (u8) merged_datum; + break; + + case 2: + MOVE_UNALIGNED16_TO_16 (&(((u16 *) buffer)[this_field_datum_offset]), &merged_datum); + break; + + case 4: + MOVE_UNALIGNED32_TO_32 (&(((u32 *) buffer)[this_field_datum_offset]), &merged_datum); + break; + } + + this_field_byte_offset = 1; + this_field_datum_offset = 1; + } + + else { + /* We need to get more raw data to complete one or more field data */ + + while (this_field_datum_offset < datum_length) { + /* + * Get the next raw datum, it contains bits of the current + * field datum + */ + + status = acpi_aml_read_field_data (obj_desc, + this_field_byte_offset + byte_granularity, + bit_granularity, &this_raw_datum); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Before merging the data, make sure the unused bits are clear */ + + switch (byte_granularity) + { + case 1: + this_raw_datum &= 0x000000FF; + previous_raw_datum &= 0x000000FF; + break; + + case 2: + this_raw_datum &= 0x0000FFFF; + previous_raw_datum &= 0x0000FFFF; + break; + } + + /* + * Put together bits of the two raw data to make a complete + * field datum + */ + + if (obj_desc->field.bit_offset != 0) { + merged_datum = + (previous_raw_datum >> obj_desc->field.bit_offset) | + (this_raw_datum << (bit_granularity - obj_desc->field.bit_offset)); + } + + else { + merged_datum = previous_raw_datum; + } + + /* + * Now store the datum in the caller's buffer, according to + * the data type + */ + + switch (byte_granularity) + { + case 1: + ((u8 *) buffer) [this_field_datum_offset] = (u8) merged_datum; + break; + + case 2: + MOVE_UNALIGNED16_TO_16 (&(((u16 *) buffer) [this_field_datum_offset]), &merged_datum); + break; + + case 4: + MOVE_UNALIGNED32_TO_32 (&(((u32 *) buffer) [this_field_datum_offset]), &merged_datum); + break; + } + + + /* + * Save the most recent datum since it contains bits of + * the *next* field datum + */ + + previous_raw_datum = this_raw_datum; + + this_field_byte_offset += byte_granularity; + this_field_datum_offset++; + + } /* while */ + } + +cleanup: + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_write_field_data + * + * PARAMETERS: *Obj_desc - Field to be set + * Value - Value to store + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Store the value into the given field + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_write_field_data ( + ACPI_OBJECT_INTERNAL *obj_desc, + u32 field_byte_offset, + u32 field_bit_width, + u32 value) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *rgn_desc = NULL; + u32 address; + s32 field_byte_width; + + + /* Obj_desc is validated by callers */ + + if (obj_desc) { + rgn_desc = obj_desc->field.container; + } + + field_byte_width = DIV_8 (field_bit_width); + status = acpi_aml_setup_field (obj_desc, rgn_desc, field_bit_width); + if (AE_OK != status) { + return (status); + } + + + /* + * Round offset down to next multiple of + * field width, add region base address and offset within the field + */ + + address = rgn_desc->region.address + + (obj_desc->field.offset & ~((u32) field_byte_width - 1)) + + field_byte_offset; + + + /* Invoke the appropriate Address_space/Op_region handler */ + + status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_WRITE, + address, field_bit_width, &value); + + + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_write_field_data_with_update_rule + * + * PARAMETERS: *Obj_desc - Field to be set + * Value - Value to store + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Apply the field update rule to a field write + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_write_field_data_with_update_rule ( + ACPI_OBJECT_INTERNAL *obj_desc, + u32 mask, + u32 field_value, + u32 this_field_byte_offset, + u32 bit_granularity) +{ + ACPI_STATUS status = AE_OK; + u32 merged_value; + u32 current_value; + + + /* Start with the new bits */ + + merged_value = field_value; + + /* Check if update rule needs to be applied (not if mask is all ones) */ + + + /* Decode the update rule */ + + switch (obj_desc->field.update_rule) + { + + case UPDATE_PRESERVE: + + /* + * Read the current contents of the byte/word/dword containing + * the field, and merge with the new field value. + */ + status = acpi_aml_read_field_data (obj_desc, this_field_byte_offset, + bit_granularity, ¤t_value); + merged_value |= (current_value & ~mask); + break; + + + case UPDATE_WRITE_AS_ONES: + + /* Set positions outside the field to all ones */ + + merged_value |= ~mask; + break; + + + case UPDATE_WRITE_AS_ZEROS: + + /* Set positions outside the field to all zeros */ + + merged_value &= mask; + break; + + + default: + status = AE_AML_OPERAND_VALUE; + } + + + /* Write the merged value */ + + if (ACPI_SUCCESS (status)) { + status = acpi_aml_write_field_data (obj_desc, this_field_byte_offset, + bit_granularity, merged_value); + } + + return status; +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_aml_write_field + * + * PARAMETERS: *Obj_desc - Field to be set + * Value - Value to store + * Field_bit_width - Field Width in bits (8, 16, or 32) + * + * RETURN: Status + * + * DESCRIPTION: Store the value into the given field + * + ****************************************************************************/ + +ACPI_STATUS +acpi_aml_write_field ( + ACPI_OBJECT_INTERNAL *obj_desc, + void *buffer, + u32 buffer_length, + u32 byte_length, + u32 datum_length, + u32 bit_granularity, + u32 byte_granularity) +{ + ACPI_STATUS status; + u32 this_field_byte_offset; + u32 this_field_datum_offset; + u32 mask; + u32 merged_datum; + u32 previous_raw_datum; + u32 this_raw_datum; + u32 field_value; + u32 valid_field_bits; + + + /* + * Break the request into up to three parts: + * non-aligned part at start, aligned part in middle, non-aligned part + * at end --- Just like an I/O request --- + */ + + this_field_byte_offset = 0; + this_field_datum_offset= 0; + + /* Get a datum */ + + switch (byte_granularity) + { + case 1: + previous_raw_datum = ((u8 *) buffer) [this_field_datum_offset]; + break; + + case 2: + MOVE_UNALIGNED16_TO_32 (&previous_raw_datum, &(((u16 *) buffer) [this_field_datum_offset])); + break; + + case 4: + MOVE_UNALIGNED32_TO_32 (&previous_raw_datum, &(((u32 *) buffer) [this_field_datum_offset])); + break; + + default: + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + + + /* + * Write a partial field datum if field does not begin on a datum boundary + * + * Construct Mask with 1 bits where the field is, 0 bits elsewhere + * + * 1) Bits above the field + */ + + mask = (((u32)(-1)) << (u32)obj_desc->field.bit_offset); + + /* 2) Only the bottom 5 bits are valid for a shift operation. */ + + if ((obj_desc->field.bit_offset + obj_desc->field_unit.length) < 32) { + /* Bits above the field */ + + mask &= (~(((u32)(-1)) << ((u32)obj_desc->field.bit_offset + + (u32)obj_desc->field_unit.length))); + } + + /* 3) Shift and mask the value into the field position */ + + field_value = (previous_raw_datum << obj_desc->field.bit_offset) & mask; + + status = acpi_aml_write_field_data_with_update_rule (obj_desc, mask, field_value, + this_field_byte_offset, + bit_granularity); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + + /* If the field fits within one datum, we are done. */ + + if ((datum_length == 1) && + ((obj_desc->field.bit_offset + obj_desc->field_unit.length) <= + (u16) bit_granularity)) + { + goto cleanup; + } + + /* + * We don't need to worry about the update rule for these data, because + * all of the bits are part of the field. + * + * Can't write the last datum, however, because it might contain bits that + * are not part of the field -- the update rule must be applied. + */ + + while (this_field_datum_offset < (datum_length - 1)) { + this_field_datum_offset++; + + /* Get the next raw datum, it contains bits of the current field datum... */ + + switch (byte_granularity) + { + case 1: + this_raw_datum = ((u8 *) buffer) [this_field_datum_offset]; + break; + + case 2: + MOVE_UNALIGNED16_TO_32 (&this_raw_datum, &(((u16 *) buffer) [this_field_datum_offset])); + break; + + case 4: + MOVE_UNALIGNED32_TO_32 (&this_raw_datum, &(((u32 *) buffer) [this_field_datum_offset])); + break; + + default: + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + + /* + * Put together bits of the two raw data to make a complete field + * datum + */ + + if (obj_desc->field.bit_offset != 0) { + merged_datum = + (previous_raw_datum >> (bit_granularity - obj_desc->field.bit_offset)) | + (this_raw_datum << obj_desc->field.bit_offset); + } + + else { + merged_datum = this_raw_datum; + } + + /* Now write the completed datum */ + + + status = acpi_aml_write_field_data (obj_desc, + this_field_byte_offset + byte_granularity, + bit_granularity, merged_datum); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + + /* + * Save the most recent datum since it contains bits of + * the *next* field datum + */ + + previous_raw_datum = this_raw_datum; + + this_field_byte_offset += byte_granularity; + + } /* while */ + + + /* Write a partial field datum if field does not end on a datum boundary */ + + if ((obj_desc->field_unit.length + obj_desc->field_unit.bit_offset) % + bit_granularity) + { + switch (byte_granularity) + { + case 1: + this_raw_datum = ((u8 *) buffer) [this_field_datum_offset]; + break; + + case 2: + MOVE_UNALIGNED16_TO_32 (&this_raw_datum, &(((u16 *) buffer) [this_field_datum_offset])); + break; + + case 4: + MOVE_UNALIGNED32_TO_32 (&this_raw_datum, &(((u32 *) buffer) [this_field_datum_offset])); + break; + } + + /* Construct Mask with 1 bits where the field is, 0 bits elsewhere */ + + valid_field_bits = ((obj_desc->field_unit.length % bit_granularity) + + obj_desc->field.bit_offset); + + mask = (((u32) 1 << valid_field_bits) - (u32) 1); + + /* Shift and mask the value into the field position */ + + field_value = (previous_raw_datum >> + (bit_granularity - obj_desc->field.bit_offset)) & mask; + + status = acpi_aml_write_field_data_with_update_rule (obj_desc, mask, field_value, this_field_byte_offset + 1, + bit_granularity); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + } + + +cleanup: + + return (status); +} + + diff --git a/drivers/acpi/interpreter/ammisc.c b/drivers/acpi/interpreter/ammisc.c new file mode 100644 index 000000000..cc5d24c9e --- /dev/null +++ b/drivers/acpi/interpreter/ammisc.c @@ -0,0 +1,533 @@ + +/****************************************************************************** + * + * Module Name: ammisc - ACPI AML (p-code) execution - specific opcodes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "interp.h" +#include "amlcode.h" +#include "dispatch.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("ammisc"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_fatal + * + * PARAMETERS: none + * + * RETURN: Status. If the OS returns from the OSD call, we just keep + * on going. + * + * DESCRIPTION: Execute Fatal operator + * + * ACPI SPECIFICATION REFERENCES: + * Def_fatal := Fatal_op Fatal_type Fatal_code Fatal_arg + * Fatal_type := Byte_data + * Fatal_code := DWord_data + * Fatal_arg := Term_arg=>Integer + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_fatal ( + ACPI_WALK_STATE *walk_state) +{ + ACPI_OBJECT_INTERNAL *type_desc; + ACPI_OBJECT_INTERNAL *code_desc; + ACPI_OBJECT_INTERNAL *arg_desc; + ACPI_STATUS status; + + + /* Resolve operands */ + + status = acpi_aml_resolve_operands (AML_FATAL_OP, WALK_OPERANDS); + /* Get operands */ + + status |= acpi_ds_obj_stack_pop_object (&arg_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&code_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&type_desc, walk_state); + if (status != AE_OK) { + /* invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + (u16) AML_FATAL_OP, WALK_OPERANDS, 3); + goto cleanup; + } + + + /* Def_fatal := Fatal_op Fatal_type Fatal_code Fatal_arg */ + + + /* + * TBD: [Unhandled] call OSD interface to notify OS of fatal error + * requiring shutdown! + */ + + +cleanup: + + /* Free the operands */ + + acpi_cm_remove_reference (arg_desc); + acpi_cm_remove_reference (code_desc); + acpi_cm_remove_reference (type_desc); + + + /* If we get back from the OS call, we might as well keep going. */ + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_index + * + * PARAMETERS: none + * + * RETURN: Status + * + * DESCRIPTION: Execute Index operator + * + * ALLOCATION: Deletes one operand descriptor -- other remains on stack + * + * ACPI SPECIFICATION REFERENCES: + * Def_index := Index_op Buff_pkg_obj Index_value Result + * Index_value := Term_arg=>Integer + * Name_string := <Root_char Name_path> | <Prefix_path Name_path> + * Result := Super_name + * Super_name := Name_string | Arg_obj | Local_obj | Debug_obj | Def_index + * Local4_op | Local5_op | Local6_op | Local7_op + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_index ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *idx_desc; + ACPI_OBJECT_INTERNAL *res_desc; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_OBJECT_INTERNAL *tmp_desc; + ACPI_STATUS status; + + + /* Resolve operands */ + /* First operand can be either a package or a buffer */ + + status = acpi_aml_resolve_operands (AML_INDEX_OP, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&res_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&idx_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + (u16) AML_INDEX_OP, WALK_OPERANDS, 3); + goto cleanup; + } + + + /* Create the internal return object */ + + ret_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + + /* + * At this point, the Obj_desc operand is either a Package or a Buffer + */ + + if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { + /* Object to be indexed is a Package */ + + if (idx_desc->number.value >= obj_desc->package.count) { + status = AE_AML_PACKAGE_LIMIT; + goto cleanup; + } + + if ((res_desc->common.type == INTERNAL_TYPE_REFERENCE) && + (res_desc->reference.op_code == AML_ZERO_OP)) + { + /* + * There is no actual result descriptor (the Zero_op Result + * descriptor is a placeholder), so just delete the placeholder and + * return a reference to the package element + */ + + acpi_cm_remove_reference (res_desc); + } + + else { + /* + * Each element of the package is an internal object. Get the one + * we are after. + */ + + tmp_desc = obj_desc->package.elements[idx_desc->number.value]; + ret_desc->reference.op_code = AML_INDEX_OP; + ret_desc->reference.target_type = tmp_desc->common.type; + ret_desc->reference.object = tmp_desc; + + status = acpi_aml_exec_store (ret_desc, res_desc); + ret_desc->reference.object = NULL; + } + + /* + * The local return object must always be a reference to the package element, + * not the element itself. + */ + ret_desc->reference.op_code = AML_INDEX_OP; + ret_desc->reference.target_type = ACPI_TYPE_PACKAGE; + ret_desc->reference.where = &obj_desc->package.elements[idx_desc->number.value]; + } + + else { + /* Object to be indexed is a Buffer */ + + if (idx_desc->number.value >= obj_desc->buffer.length) { + status = AE_AML_BUFFER_LIMIT; + goto cleanup; + } + + ret_desc->reference.op_code = AML_INDEX_OP; + ret_desc->reference.target_type = ACPI_TYPE_BUFFER_FIELD; + ret_desc->reference.object = obj_desc; + ret_desc->reference.offset = idx_desc->number.value; + + status = acpi_aml_exec_store (ret_desc, res_desc); + } + + +cleanup: + + /* Always delete operands */ + + acpi_cm_remove_reference (obj_desc); + acpi_cm_remove_reference (idx_desc); + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (res_desc); + + if (ret_desc) { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + } + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_match + * + * PARAMETERS: none + * + * RETURN: Status + * + * DESCRIPTION: Execute Match operator + * + * ACPI SPECIFICATION REFERENCES: + * Def_match := Match_op Search_pkg Opcode1 Operand1 + * Opcode2 Operand2 Start_index + * Opcode1 := Byte_data: MTR, MEQ, MLE, MLT, MGE, or MGT + * Opcode2 := Byte_data: MTR, MEQ, MLE, MLT, MGE, or MGT + * Operand1 := Term_arg=>Integer + * Operand2 := Term_arg=>Integer + * Search_pkg := Term_arg=>Package_object + * Start_index := Term_arg=>Integer + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_match ( + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *pkg_desc; + ACPI_OBJECT_INTERNAL *op1_desc; + ACPI_OBJECT_INTERNAL *V1_desc; + ACPI_OBJECT_INTERNAL *op2_desc; + ACPI_OBJECT_INTERNAL *V2_desc; + ACPI_OBJECT_INTERNAL *start_desc; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_STATUS status; + u32 index; + u32 match_value = (u32) -1; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (AML_MATCH_OP, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&start_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&V2_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&op2_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&V1_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&op1_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&pkg_desc, walk_state); + + if (status != AE_OK) { + /* Invalid parameters on object stack */ + + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + (u16) AML_MATCH_OP, WALK_OPERANDS, 6); + goto cleanup; + } + + /* Validate match comparison sub-opcodes */ + + if ((op1_desc->number.value > MAX_MATCH_OPERATOR) || + (op2_desc->number.value > MAX_MATCH_OPERATOR)) + { + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + + index = start_desc->number.value; + if (index >= (u32) pkg_desc->package.count) { + status = AE_AML_PACKAGE_LIMIT; + goto cleanup; + } + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + + } + + /* + * Examine each element until a match is found. Within the loop, + * "continue" signifies that the current element does not match + * and the next should be examined. + * Upon finding a match, the loop will terminate via "break" at + * the bottom. If it terminates "normally", Match_value will be -1 + * (its initial value) indicating that no match was found. When + * returned as a Number, this will produce the Ones value as specified. + */ + + for ( ; index < pkg_desc->package.count; ++index) { + /* + * Treat any NULL or non-numeric elements as non-matching. + * TBD [Unhandled] - if an element is a Name, + * should we examine its value? + */ + if (!pkg_desc->package.elements[index] || + ACPI_TYPE_NUMBER != pkg_desc->package.elements[index]->common.type) + { + continue; + } + + /* + * Within these switch statements: + * "break" (exit from the switch) signifies a match; + * "continue" (proceed to next iteration of enclosing + * "for" loop) signifies a non-match. + */ + switch (op1_desc->number.value) + { + + case MATCH_MTR: /* always true */ + + break; + + + case MATCH_MEQ: /* true if equal */ + + if (pkg_desc->package.elements[index]->number.value + != V1_desc->number.value) + { + continue; + } + break; + + + case MATCH_MLE: /* true if less than or equal */ + + if (pkg_desc->package.elements[index]->number.value + > V1_desc->number.value) + { + continue; + } + break; + + + case MATCH_MLT: /* true if less than */ + + if (pkg_desc->package.elements[index]->number.value + >= V1_desc->number.value) + { + continue; + } + break; + + + case MATCH_MGE: /* true if greater than or equal */ + + if (pkg_desc->package.elements[index]->number.value + < V1_desc->number.value) + { + continue; + } + break; + + + case MATCH_MGT: /* true if greater than */ + + if (pkg_desc->package.elements[index]->number.value + <= V1_desc->number.value) + { + continue; + } + break; + + + default: /* undefined */ + + continue; + } + + + switch(op2_desc->number.value) + { + + case MATCH_MTR: + + break; + + + case MATCH_MEQ: + + if (pkg_desc->package.elements[index]->number.value + != V2_desc->number.value) + { + continue; + } + break; + + + case MATCH_MLE: + + if (pkg_desc->package.elements[index]->number.value + > V2_desc->number.value) + { + continue; + } + break; + + + case MATCH_MLT: + + if (pkg_desc->package.elements[index]->number.value + >= V2_desc->number.value) + { + continue; + } + break; + + + case MATCH_MGE: + + if (pkg_desc->package.elements[index]->number.value + < V2_desc->number.value) + { + continue; + } + break; + + + case MATCH_MGT: + + if (pkg_desc->package.elements[index]->number.value + <= V2_desc->number.value) + { + continue; + } + break; + + + default: + + continue; + } + + /* Match found: exit from loop */ + + match_value = index; + break; + } + + /* Match_value is the return value */ + + ret_desc->number.value = match_value; + + +cleanup: + + /* Free the operands */ + + acpi_cm_remove_reference (start_desc); + acpi_cm_remove_reference (V2_desc); + acpi_cm_remove_reference (op2_desc); + acpi_cm_remove_reference (V1_desc); + acpi_cm_remove_reference (op1_desc); + acpi_cm_remove_reference (pkg_desc); + + + /* Delete return object on error */ + + if (ACPI_FAILURE (status) && + (ret_desc)) + { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} diff --git a/drivers/acpi/interpreter/ammonad.c b/drivers/acpi/interpreter/ammonad.c new file mode 100644 index 000000000..b8ab73097 --- /dev/null +++ b/drivers/acpi/interpreter/ammonad.c @@ -0,0 +1,954 @@ + +/****************************************************************************** + * + * Module Name: ammonad - ACPI AML (p-code) execution for monadic operators + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("ammonad"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_get_object_reference + * + * PARAMETERS: Obj_desc - Create a reference to this object + * Ret_desc - Where to store the reference + * + * RETURN: Status + * + * DESCRIPTION: Obtain and return a "reference" to the target object + * Common code for the Ref_of_op and the Cond_ref_of_op. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_get_object_reference ( + ACPI_OBJECT_INTERNAL *obj_desc, + ACPI_OBJECT_INTERNAL **ret_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_INTERNAL)) { + if (obj_desc->common.type != INTERNAL_TYPE_REFERENCE) { + *ret_desc = NULL; + status = AE_TYPE; + goto cleanup; + } + + /* + * Not a Name -- an indirect name pointer would have + * been converted to a direct name pointer in Acpi_aml_resolve_operands + */ + switch (obj_desc->reference.op_code) + { + case AML_LOCAL_OP: + + *ret_desc = (void *) acpi_ds_method_data_get_nte (MTH_TYPE_LOCAL, + (obj_desc->reference.offset)); + break; + + + case AML_ARG_OP: + + *ret_desc = (void *) acpi_ds_method_data_get_nte (MTH_TYPE_ARG, + (obj_desc->reference.offset)); + break; + + + default: + + *ret_desc = NULL; + status = AE_AML_INTERNAL; + goto cleanup; + } + + } + + else if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + /* Must be a named object; Just return the NTE */ + + *ret_desc = obj_desc; + } + + else { + *ret_desc = NULL; + status = AE_TYPE; + } + + +cleanup: + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_monadic1 + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 1 monadic operator with numeric operand on + * object stack + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_monadic1 ( + u16 opcode, + ACPI_WALK_STATE *walk_state) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + opcode, WALK_OPERANDS, 1); + goto cleanup; + } + + + /* Examine the opcode */ + + switch (opcode) + { + + /* Def_release := Release_op Mutex_object */ + + case AML_RELEASE_OP: + + status = acpi_aml_system_release_mutex (obj_desc); + break; + + + /* Def_reset := Reset_op Acpi_event_object */ + + case AML_RESET_OP: + + status = acpi_aml_system_reset_event (obj_desc); + break; + + + /* Def_signal := Signal_op Acpi_event_object */ + + case AML_SIGNAL_OP: + + status = acpi_aml_system_signal_event (obj_desc); + break; + + + /* Def_sleep := Sleep_op Msec_time */ + + case AML_SLEEP_OP: + + acpi_aml_system_do_suspend (obj_desc->number.value); + break; + + + /* Def_stall := Stall_op Usec_time */ + + case AML_STALL_OP: + + acpi_aml_system_do_stall (obj_desc->number.value); + break; + + + /* Unknown opcode */ + + default: + + status = AE_AML_BAD_OPCODE; + break; + + } /* switch */ + + +cleanup: + + /* Always delete the operand */ + + acpi_cm_remove_reference (obj_desc); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_monadic2_r + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 2 monadic operator with numeric operand and + * result operand on operand stack + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_monadic2_r ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *res_desc; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_OBJECT_INTERNAL *ret_desc2 = NULL; + u32 res_val; + ACPI_STATUS status; + s32 d0; + s32 d1; + s32 d2; + s32 d3; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&res_desc, walk_state); + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + opcode, WALK_OPERANDS, 2); + goto cleanup; + } + + + /* Create a return object of type NUMBER for most opcodes */ + + switch (opcode) + { + case AML_BIT_NOT_OP: + case AML_FIND_SET_LEFT_BIT_OP: + case AML_FIND_SET_RIGHT_BIT_OP: + case AML_FROM_BCDOP: + case AML_TO_BCDOP: + case AML_COND_REF_OF_OP: + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + break; + } + + + switch (opcode) + { + /* Def_not := Not_op Operand Result */ + + case AML_BIT_NOT_OP: + + ret_desc->number.value = ~obj_desc->number.value; + break; + + + /* Def_find_set_left_bit := Find_set_left_bit_op Operand Result */ + + case AML_FIND_SET_LEFT_BIT_OP: + + ret_desc->number.value = obj_desc->number.value; + for (res_val = 0; ret_desc->number.value && res_val < 33; ++res_val) { + ret_desc->number.value >>= 1; + } + + ret_desc->number.value = res_val; + break; + + + /* Def_find_set_right_bit := Find_set_right_bit_op Operand Result */ + + case AML_FIND_SET_RIGHT_BIT_OP: + + ret_desc->number.value = obj_desc->number.value; + for (res_val = 0; ret_desc->number.value && res_val < 33; ++res_val) { + ret_desc->number.value <<= 1; + } + + ret_desc->number.value = res_val == 0 ? 0 : 33 - res_val; + break; + + + /* Def_from_bDC := From_bCDOp BCDValue Result */ + + case AML_FROM_BCDOP: + + d0 = (s32) (obj_desc->number.value & 15); + d1 = (s32) (obj_desc->number.value >> 4 & 15); + d2 = (s32) (obj_desc->number.value >> 8 & 15); + d3 = (s32) (obj_desc->number.value >> 12 & 15); + + if (d0 > 9 || d1 > 9 || d2 > 9 || d3 > 9) { + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + + ret_desc->number.value = d0 + d1 * 10 + d2 * 100 + d3 * 1000; + break; + + + /* Def_to_bDC := To_bCDOp Operand Result */ + + case AML_TO_BCDOP: + + + if (obj_desc->number.value > 9999) { + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + + ret_desc->number.value + = obj_desc->number.value % 10 + + (obj_desc->number.value / 10 % 10 << 4) + + (obj_desc->number.value / 100 % 10 << 8) + + (obj_desc->number.value / 1000 % 10 << 12); + + break; + + + /* Def_cond_ref_of := Cond_ref_of_op Source_object Result */ + + case AML_COND_REF_OF_OP: + + /* + * This op is a little strange because the internal return value is + * different than the return value stored in the result descriptor + * (There are really two return values) + */ + + if ((ACPI_NAMED_OBJECT*) obj_desc == acpi_gbl_root_object) { + /* + * This means that the object does not exist in the namespace, + * return FALSE + */ + + ret_desc->number.value = 0; + + /* + * Must delete the result descriptor since there is no reference + * being returned + */ + + acpi_cm_remove_reference (res_desc); + goto cleanup; + } + + /* Get the object reference and store it */ + + status = acpi_aml_get_object_reference (obj_desc, &ret_desc2); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_aml_exec_store (ret_desc2, res_desc); + + /* The object exists in the namespace, return TRUE */ + + ret_desc->number.value = (u32) -1; + goto cleanup; + break; + + + case AML_STORE_OP: + + /* + * A store operand is typically a number, string, buffer or lvalue + * TBD: [Unhandled] What about a store to a package? + */ + + /* + * Do the store, and be careful about deleting the source object, + * since the object itself may have been stored. + */ + + status = acpi_aml_exec_store (obj_desc, res_desc); + if (ACPI_FAILURE (status)) { + /* On failure, just delete the Obj_desc */ + + acpi_cm_remove_reference (obj_desc); + } + + else { + /* + * Normally, we would remove a reference on the Obj_desc parameter; + * But since it is being used as the internal return object + * (meaning we would normally increment it), the two cancel out, + * and we simply don't do anything. + */ + *return_desc = obj_desc; + } + + obj_desc = NULL; + return (status); + + break; + + + case AML_DEBUG_OP: + + /* Reference, returning an Reference */ + + return (AE_OK); + break; + + + /* + * These are obsolete opcodes + */ + + /* Def_shift_left_bit := Shift_left_bit_op Source Bit_num */ + /* Def_shift_right_bit := Shift_right_bit_op Source Bit_num */ + + case AML_SHIFT_LEFT_BIT_OP: + case AML_SHIFT_RIGHT_BIT_OP: + + status = AE_SUPPORT; + goto cleanup; + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + status = acpi_aml_exec_store (ret_desc, res_desc); + + +cleanup: + /* Always delete the operand object */ + + acpi_cm_remove_reference (obj_desc); + + /* Delete return object(s) on error */ + + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (res_desc); /* Result descriptor */ + if (ret_desc) { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + } + + /* Set the return object and exit */ + + *return_desc = ret_desc; + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_monadic2 + * + * PARAMETERS: Opcode - The opcode to be executed + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 2 monadic operator with numeric operand: + * Deref_of_op, Ref_of_op, Size_of_op, Type_op, Increment_op, + * Decrement_op, LNot_op, + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_monadic2 ( + u16 opcode, + ACPI_WALK_STATE *walk_state, + ACPI_OBJECT_INTERNAL **return_desc) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *tmp_desc; + ACPI_OBJECT_INTERNAL *ret_desc = NULL; + ACPI_STATUS status; + u32 type; + u32 value; + + + /* Resolve all operands */ + + status = acpi_aml_resolve_operands (opcode, WALK_OPERANDS); + /* Get all operands */ + + status |= acpi_ds_obj_stack_pop_object (&obj_desc, walk_state); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + opcode, WALK_OPERANDS, 1); + goto cleanup; + } + + + /* Get the operand and decode the opcode */ + + + switch (opcode) + { + + /* Def_lNot := LNot_op Operand */ + + case AML_LNOT_OP: + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + ret_desc->number.value = !obj_desc->number.value; + break; + + + /* Def_decrement := Decrement_op Target */ + /* Def_increment := Increment_op Target */ + + case AML_DECREMENT_OP: + case AML_INCREMENT_OP: + + /* + * Since we are expecting an Reference on the top of the stack, it + * can be either an NTE or an internal object. + * + * TBD: [Future] This may be the prototype code for all cases where + * an Reference is expected!! 10/99 + */ + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + ret_desc = obj_desc; + } + + else { + /* + * Duplicate the Reference in a new object so that we can resolve it + * without destroying the original Reference object + */ + + ret_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + ret_desc->reference.op_code = obj_desc->reference.op_code; + ret_desc->reference.offset = obj_desc->reference.offset; + ret_desc->reference.object = obj_desc->reference.object; + } + + + /* + * Convert the Ret_desc Reference to a Number + * (This deletes the original Ret_desc) + */ + + status = acpi_aml_resolve_operands (AML_LNOT_OP, &ret_desc); + if (status != AE_OK) { + acpi_aml_append_operand_diag (_THIS_MODULE, __LINE__, + opcode, WALK_OPERANDS, 1); + goto cleanup; + } + + /* Do the actual increment or decrement */ + + if (AML_INCREMENT_OP == opcode) { + ret_desc->number.value++; + } + else { + ret_desc->number.value--; + } + + /* Store the result back in the original descriptor */ + + status = acpi_aml_exec_store (ret_desc, obj_desc); + + /* Objdesc was just deleted (because it is an Reference) */ + + obj_desc = NULL; + + break; + + + /* Def_object_type := Object_type_op Source_object */ + + case AML_TYPE_OP: + + if (INTERNAL_TYPE_REFERENCE == obj_desc->common.type) { + /* + * Not a Name -- an indirect name pointer would have + * been converted to a direct name pointer in Resolve_operands + */ + switch (obj_desc->reference.op_code) + { + case AML_ZERO_OP: + case AML_ONE_OP: + case AML_ONES_OP: + + /* Constants are of type Number */ + + type = ACPI_TYPE_NUMBER; + break; + + + case AML_DEBUG_OP: + + /* Per 1.0b spec, Debug object is of type Debug_object */ + + type = ACPI_TYPE_DEBUG_OBJECT; + break; + + + case AML_INDEX_OP: + + /* Get the type of this reference (index into another object) */ + + type = obj_desc->reference.target_type; + if (type == ACPI_TYPE_PACKAGE) { + /* + * The main object is a package, we want to get the type + * of the individual package element that is referenced by + * the index. + */ + type = (*(obj_desc->reference.where))->common.type; + } + + break; + + + case AML_LOCAL_OP: + + type = acpi_ds_method_data_get_type (MTH_TYPE_LOCAL, + (obj_desc->reference.offset)); + break; + + + case AML_ARG_OP: + + type = acpi_ds_method_data_get_type (MTH_TYPE_ARG, + (obj_desc->reference.offset)); + break; + + + default: + + status = AE_AML_INTERNAL; + goto cleanup; + } + } + + else { + /* + * Since we passed Acpi_aml_resolve_operands("l") and it's not a + * Reference, it must be a direct name pointer. + */ + type = acpi_ns_get_type ((ACPI_HANDLE) obj_desc); + } + + /* Allocate a descriptor to hold the type. */ + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + ret_desc->number.value = type; + break; + + + /* Def_size_of := Size_of_op Source_object */ + + case AML_SIZE_OF_OP: + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + obj_desc = acpi_ns_get_attached_object (obj_desc); + } + + if (!obj_desc) { + value = 0; + } + + else { + switch (obj_desc->common.type) + { + + case ACPI_TYPE_BUFFER: + + value = obj_desc->buffer.length; + break; + + + case ACPI_TYPE_STRING: + + value = obj_desc->string.length; + break; + + + case ACPI_TYPE_PACKAGE: + + value = obj_desc->package.count; + break; + + case INTERNAL_TYPE_REFERENCE: + + value = 4; + break; + + default: + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } + + /* + * Now that we have the size of the object, create a result + * object to hold the value + */ + + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + ret_desc->number.value = value; + break; + + + /* Def_ref_of := Ref_of_op Source_object */ + + case AML_REF_OF_OP: + + status = acpi_aml_get_object_reference (obj_desc, &ret_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + break; + + + /* Def_deref_of := Deref_of_op Obj_reference */ + + case AML_DEREF_OF_OP: + + + /* Check for a method local or argument */ + + if (!VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + /* + * Must resolve/dereference the local/arg reference first + */ + switch (obj_desc->reference.op_code) + { + /* Set Obj_desc to the value of the local/arg */ + + case AML_LOCAL_OP: + + acpi_ds_method_data_get_value (MTH_TYPE_LOCAL, + (obj_desc->reference.offset), &tmp_desc); + + /* + * Delete our reference to the input object and + * point to the object just retrieved + */ + acpi_cm_remove_reference (obj_desc); + obj_desc = tmp_desc; + break; + + + case AML_ARG_OP: + + acpi_ds_method_data_get_value (MTH_TYPE_ARG, + (obj_desc->reference.offset), &tmp_desc); + + /* + * Delete our reference to the input object and + * point to the object just retrieved + */ + acpi_cm_remove_reference (obj_desc); + obj_desc = tmp_desc; + break; + + default: + + /* Index op - handled below */ + break; + } + } + + + /* Obj_desc may have changed from the code above */ + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + /* Get the actual object from the NTE (This is the dereference) */ + + ret_desc = ((ACPI_NAMED_OBJECT*) obj_desc)->object; + + /* Returning a pointer to the object, add another reference! */ + + acpi_cm_add_reference (ret_desc); + } + + else { + /* + * This must be a reference object produced by the Index + * ASL operation -- check internal opcode + */ + + if ((obj_desc->reference.op_code != AML_INDEX_OP) && + (obj_desc->reference.op_code != AML_REF_OF_OP)) + { + status = AE_TYPE; + goto cleanup; + } + + + switch (obj_desc->reference.op_code) + { + case AML_INDEX_OP: + + /* + * Supported target types for the Index operator are + * 1) A Buffer + * 2) A Package + */ + + if (obj_desc->reference.target_type == ACPI_TYPE_BUFFER_FIELD) { + /* + * The target is a buffer, we must create a new object that + * contains one element of the buffer, the element pointed + * to by the index. + * + * NOTE: index into a buffer is NOT a pointer to a + * sub-buffer of the main buffer, it is only a pointer to a + * single element (byte) of the buffer! + */ + ret_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!ret_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + tmp_desc = obj_desc->reference.object; + ret_desc->number.value = + tmp_desc->buffer.pointer[obj_desc->reference.offset]; + + /* TBD: [Investigate] (see below) Don't add an additional + * ref! + */ + } + + else if (obj_desc->reference.target_type == ACPI_TYPE_PACKAGE) { + /* + * The target is a package, we want to return the referenced + * element of the package. We must add another reference to + * this object, however. + */ + + ret_desc = *(obj_desc->reference.where); + if (!ret_desc) { + /* + * We can't return a NULL dereferenced value. This is + * an uninitialized package element and is thus a + * severe error. + */ + + status = AE_AML_UNINITIALIZED_ELEMENT; + goto cleanup; + } + + acpi_cm_add_reference (ret_desc); + } + + else { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + break; + + + case AML_REF_OF_OP: + + ret_desc = obj_desc->reference.object; + + /* Add another reference to the object! */ + + acpi_cm_add_reference (ret_desc); + break; + } + } + + break; + + + default: + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + +cleanup: + + if (obj_desc) { + acpi_cm_remove_reference (obj_desc); + } + + /* Delete return object on error */ + + if (ACPI_FAILURE (status) && + (ret_desc)) + { + acpi_cm_remove_reference (ret_desc); + ret_desc = NULL; + } + + *return_desc = ret_desc; + return (status); +} + diff --git a/drivers/acpi/interpreter/amnames.c b/drivers/acpi/interpreter/amnames.c new file mode 100644 index 000000000..f6222b3b9 --- /dev/null +++ b/drivers/acpi/interpreter/amnames.c @@ -0,0 +1,440 @@ + +/****************************************************************************** + * + * Module Name: amnames - interpreter/scanner name load/execute + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amnames"); + + +/* AML Package Length encodings */ + +#define ACPI_AML_PACKAGE_TYPE1 0x40 +#define ACPI_AML_PACKAGE_TYPE2 0x4000 +#define ACPI_AML_PACKAGE_TYPE3 0x400000 +#define ACPI_AML_PACKAGE_TYPE4 0x40000000 + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_allocate_name_string + * + * PARAMETERS: Prefix_count - Count of parent levels. Special cases: + * (-1) = root, 0 = none + * Num_name_segs - count of 4-character name segments + * + * RETURN: A pointer to the allocated string segment. This segment must + * be deleted by the caller. + * + * DESCRIPTION: Allocate a buffer for a name string. Ensure allocated name + * string is long enough, and set up prefix if any. + * + ******************************************************************************/ + +char * +acpi_aml_allocate_name_string ( + u32 prefix_count, + u32 num_name_segs) +{ + char *temp_ptr; + char *name_string; + u32 size_needed; + + + /* + * Allow room for all \ and ^ prefixes, all segments, and a Multi_name_prefix. + * Also, one byte for the null terminator. + * This may actually be somewhat longer than needed. + */ + + if (prefix_count == (u32) -1) { + /* Special case for root */ + + size_needed = 1 + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } + else { + size_needed = prefix_count + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } + + /* + * Allocate a buffer for the name. + * This buffer must be deleted by the caller! + */ + + name_string = acpi_cm_allocate ((ACPI_SIZE) size_needed); + if (!name_string) { + REPORT_ERROR ("Aml_allocate_name_string: name allocation failure"); + return (NULL); + } + + temp_ptr = name_string; + + /* Set up Root or Parent prefixes if needed */ + + if (prefix_count == (u32) -1) { + *temp_ptr++ = AML_ROOT_PREFIX; + } + + else { + while (prefix_count--) { + *temp_ptr++ = AML_PARENT_PREFIX; + } + } + + + /* Set up Dual or Multi prefixes if needed */ + + if (num_name_segs > 2) { + /* Set up multi prefixes */ + + *temp_ptr++ = AML_MULTI_NAME_PREFIX_OP; + *temp_ptr++ = (char) num_name_segs; + } + + else if (2 == num_name_segs) { + /* Set up dual prefixes */ + + *temp_ptr++ = AML_DUAL_NAME_PREFIX; + } + + /* + * Terminate string following prefixes. Acpi_aml_exec_name_segment() will + * append the segment(s) + */ + + *temp_ptr = 0; + + return (name_string); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_decode_package_length + * + * PARAMETERS: Last_pkg_len - latest value decoded by Do_pkg_length() for + * most recently examined package or field + * + * RETURN: Number of bytes contained in package length encoding + * + * DESCRIPTION: Decodes the Package Length. Upper 2 bits are are used to + * tell if type 1, 2, 3, or 4. + * 0x3F = Max 1 byte encoding, + * 0xFFF = Max 2 byte encoding, + * 0xFFFFF = Max 3 Byte encoding, + * 0xFFFFFFFFF = Max 4 Byte encoding. + * + ******************************************************************************/ + +u32 +acpi_aml_decode_package_length ( + u32 last_pkg_len) +{ + u32 num_bytes = 0; + + + if (last_pkg_len < ACPI_AML_PACKAGE_TYPE1) { + num_bytes = 1; + } + + else if (last_pkg_len < ACPI_AML_PACKAGE_TYPE2) { + num_bytes = 2; + } + + else if (last_pkg_len < ACPI_AML_PACKAGE_TYPE3) { + num_bytes = 3; + } + + else if (last_pkg_len < ACPI_AML_PACKAGE_TYPE4) { + num_bytes = 4; + } + + return (num_bytes); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_name_segment + * + * PARAMETERS: Interpreter_mode - Current running mode (load1/Load2/Exec) + * + * RETURN: Status + * + * DESCRIPTION: Execute a name segment (4 bytes) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_name_segment ( + u8 **in_aml_address, + char *name_string) +{ + u8 *aml_address = *in_aml_address; + ACPI_STATUS status = AE_OK; + s32 index; + char char_buf[5]; + + + /* + * If first character is a digit, then we know that we aren't looking at a + * valid name segment + */ + + char_buf[0] = *aml_address; + + if ('0' <= char_buf[0] && char_buf[0] <= '9') { + return (AE_CTRL_PENDING); + } + + for (index = 4; + (index > 0) && (acpi_cm_valid_acpi_character (*aml_address)); + --index) + { + char_buf[4 - index] = *aml_address++; + } + + + /* Valid name segment */ + + if (0 == index) { + /* Found 4 valid characters */ + + char_buf[4] = '\0'; + + if (name_string) { + STRCAT (name_string, char_buf); + } + + } + + else if (4 == index) { + /* + * First character was not a valid name character, + * so we are looking at something other than a name. + */ + status = AE_CTRL_PENDING; + } + + else { + /* Segment started with one or more valid characters, but fewer than 4 */ + + status = AE_AML_BAD_NAME; + } + + *in_aml_address = aml_address; + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_get_name_string + * + * PARAMETERS: Data_type - Data type to be associated with this name + * + * RETURN: Status + * + * DESCRIPTION: Get a name, including any prefixes. + * + ******************************************************************************/ + + +ACPI_STATUS +acpi_aml_get_name_string ( + OBJECT_TYPE_INTERNAL data_type, + u8 *in_aml_address, + char **out_name_string, + u32 *out_name_length) +{ + ACPI_STATUS status = AE_OK; + u8 *aml_address = in_aml_address; + char *name_string = NULL; + s32 num_segments; + s32 prefix_count = 0; + u8 prefix = 0; + + + if (INTERNAL_TYPE_DEF_FIELD == data_type || + INTERNAL_TYPE_BANK_FIELD == data_type || + INTERNAL_TYPE_INDEX_FIELD == data_type) + { + /* Disallow prefixes for types associated with field names */ + + name_string = acpi_aml_allocate_name_string (0, 1); + if (!name_string) { + status = AE_NO_MEMORY; + } + else { + status = acpi_aml_exec_name_segment (&aml_address, name_string); + } + } + + else { + /* + * Data_type is not a field name. + * Examine first character of name for root or parent prefix operators + */ + + switch (*aml_address) + { + + case AML_ROOT_PREFIX: + + prefix = *aml_address++; + /* + * Remember that we have a Root_prefix -- + * see comment in Acpi_aml_allocate_name_string() + */ + prefix_count = -1; + break; + + + case AML_PARENT_PREFIX: + + /* Increment past possibly multiple parent prefixes */ + + do + { + prefix = *aml_address++; + ++prefix_count; + + } while (*aml_address == AML_PARENT_PREFIX); + + break; + + + default: + + break; + } + + + /* Examine first character of name for name segment prefix operator */ + + switch (*aml_address) + { + + case AML_DUAL_NAME_PREFIX: + + prefix = *aml_address++; + name_string = acpi_aml_allocate_name_string (prefix_count, 2); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Ensure Prefix_count != 0 to remember processing a prefix */ + + prefix_count += 2; + + status = acpi_aml_exec_name_segment (&aml_address, name_string); + if (ACPI_SUCCESS (status)) { + status = acpi_aml_exec_name_segment (&aml_address, name_string); + } + break; + + + case AML_MULTI_NAME_PREFIX_OP: + + prefix = *aml_address++; + /* Fetch count of segments remaining in name path */ + + num_segments = *aml_address++; + + name_string = acpi_aml_allocate_name_string (prefix_count, num_segments); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Ensure Prefix_count != 0 to remember processing a prefix */ + + prefix_count += 2; + + while (num_segments && + (status = acpi_aml_exec_name_segment (&aml_address, name_string)) == AE_OK) + { + --num_segments; + } + + break; + + + case 0: + + /* Null_name valid as of 8-12-98 ASL/AML Grammar Update */ + + + /* Consume the NULL byte */ + + aml_address++; + name_string = acpi_aml_allocate_name_string (prefix_count, 0); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + break; + + + default: + + /* Name segment string */ + + name_string = acpi_aml_allocate_name_string (prefix_count, 1); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + status = acpi_aml_exec_name_segment (&aml_address, name_string); + break; + + } /* Switch (Peek_op ()) */ + } + + + if (AE_CTRL_PENDING == status && prefix_count != 0) { + /* Ran out of segments after processing a prefix */ + + REPORT_ERROR ("Ran out of segments after processing a prefix"); + + status = AE_AML_BAD_NAME; + } + + + *out_name_string = name_string; + *out_name_length = (u32) (aml_address - in_aml_address); + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amprep.c b/drivers/acpi/interpreter/amprep.c new file mode 100644 index 000000000..9a5ef1706 --- /dev/null +++ b/drivers/acpi/interpreter/amprep.c @@ -0,0 +1,392 @@ + +/****************************************************************************** + * + * Module Name: amprep - ACPI AML (p-code) execution - field prep utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "parser.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amprep"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_decode_field_access_type + * + * PARAMETERS: Access - Encoded field access bits + * + * RETURN: Field granularity (8, 16, or 32) + * + * DESCRIPTION: Decode the Access_type bits of a field definition. + * + ******************************************************************************/ + +u32 +acpi_aml_decode_field_access_type ( + u32 access) +{ + + switch (access) + { + case ACCESS_ANY_ACC: + return 8; + break; + + case ACCESS_BYTE_ACC: + return 8; + break; + + case ACCESS_WORD_ACC: + return 16; + break; + + case ACCESS_DWORD_ACC: + return 32; + break; + + default: + /* Invalid field access type */ + + return 0; + } +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_prep_common_field_objec + * + * PARAMETERS: Obj_desc - The field object + * Field_flags - Access, Lock_rule, or Update_rule. + * The format of a Field_flag is described + * in the ACPI specification + * Field_position - Field position + * Field_length - Field length + * + * RETURN: Status + * + * DESCRIPTION: Initialize the areas of the field object that are common + * to the various types of fields. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_prep_common_field_object ( + ACPI_OBJECT_INTERNAL *obj_desc, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length) +{ + u32 granularity; + + + /* + * Note: the structure being initialized is the + * ACPI_COMMON_FIELD_INFO; Therefore, we can just use the Field union to + * access this common area. No structure fields outside of the common area + * are initialized by this procedure. + */ + + /* Decode the Field_flags */ + + obj_desc->field.access = (u8) ((field_flags & ACCESS_TYPE_MASK) + >> ACCESS_TYPE_SHIFT); + obj_desc->field.lock_rule = (u8) ((field_flags & LOCK_RULE_MASK) + >> LOCK_RULE_SHIFT); + obj_desc->field.update_rule = (u8) ((field_flags & UPDATE_RULE_MASK) + >> UPDATE_RULE_SHIFT); + + /* Other misc fields */ + + obj_desc->field.length = (u16) field_length; + obj_desc->field.access_attribute = field_attribute; + + /* Decode the access type so we can compute offsets */ + + granularity = acpi_aml_decode_field_access_type (obj_desc->field.access); + if (!granularity) { + return (AE_AML_OPERAND_VALUE); + } + + /* Access granularity based fields */ + + obj_desc->field.granularity = (u8) granularity; + obj_desc->field.bit_offset = (u8) (field_position % granularity); + obj_desc->field.offset = (u32) field_position / granularity; + + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_prep_def_field_value + * + * PARAMETERS: This_entry - Owning NTE + * Region - Region in which field is being defined + * Field_flags - Access, Lock_rule, or Update_rule. + * The format of a Field_flag is described + * in the ACPI specification + * Field_position - Field position + * Field_length - Field length + * + * RETURN: Status + * + * DESCRIPTION: Construct an ACPI_OBJECT_INTERNAL of type Def_field and + * connect it to the parent NTE. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_prep_def_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE region, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + s32 type; + ACPI_STATUS status; + + + /* Parameter validation */ + + if (!region) { + return (AE_AML_NO_OPERAND); + } + + type = acpi_ns_get_type (region); + if (type != ACPI_TYPE_REGION) { + return (AE_AML_OPERAND_TYPE); + } + + /* Allocate a new object */ + + obj_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_DEF_FIELD); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + + /* Obj_desc and Region valid */ + + /* Initialize areas of the object that are common to all fields */ + + status = acpi_aml_prep_common_field_object (obj_desc, field_flags, field_attribute, + field_position, field_length); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Initialize areas of the object that are specific to this field type */ + + obj_desc->field.container = acpi_ns_get_attached_object (region); + + /* An additional reference for the container */ + + acpi_cm_add_reference (obj_desc->field.container); + + + /* Debug info */ + + /* + * Store the constructed descriptor (Obj_desc) into the nte whose + * handle is on TOS, preserving the current type of that nte. + */ + status = acpi_ns_attach_object ((ACPI_HANDLE) this_entry, obj_desc, + (u8) acpi_ns_get_type ((ACPI_HANDLE) this_entry)); + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_prep_bank_field_value + * + * PARAMETERS: This_entry - Owning NTE + * Region - Region in which field is being defined + * Bank_reg - Bank selection register + * Bank_val - Value to store in selection register + * Field_flags - Access, Lock_rule, or Update_rule + * Field_position - Field position + * Field_length - Field length + * + * RETURN: Status + * + * DESCRIPTION: Construct an ACPI_OBJECT_INTERNAL of type Bank_field and + * connect it to the parent NTE. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_prep_bank_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE region, + ACPI_HANDLE bank_reg, + u32 bank_val, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + s32 type; + ACPI_STATUS status; + + + /* Parameter validation */ + + if (!region) { + return (AE_AML_NO_OPERAND); + } + + type = acpi_ns_get_type (region); + if (type != ACPI_TYPE_REGION) { + return (AE_AML_OPERAND_TYPE); + } + + /* Allocate a new object */ + + obj_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_BANK_FIELD); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* Obj_desc and Region valid */ + + /* Initialize areas of the object that are common to all fields */ + + status = acpi_aml_prep_common_field_object (obj_desc, field_flags, field_attribute, + field_position, field_length); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Initialize areas of the object that are specific to this field type */ + + obj_desc->bank_field.value = bank_val; + obj_desc->bank_field.container = acpi_ns_get_attached_object (region); + obj_desc->bank_field.bank_select = acpi_ns_get_attached_object (bank_reg); + + /* An additional reference for the container and bank select */ + /* TBD: [Restructure] is "Bank_select" ever a real internal object?? */ + + acpi_cm_add_reference (obj_desc->bank_field.container); + acpi_cm_add_reference (obj_desc->bank_field.bank_select); + + /* Debug info */ + + /* + * Store the constructed descriptor (Obj_desc) into the nte whose + * handle is on TOS, preserving the current type of that nte. + */ + status = acpi_ns_attach_object ((ACPI_HANDLE) this_entry, obj_desc, + (u8) acpi_ns_get_type ((ACPI_HANDLE) this_entry)); + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_prep_index_field_value + * + * PARAMETERS: This_entry - Owning NTE + * Index_reg - Index register + * Data_reg - Data register + * Field_flags - Access, Lock_rule, or Update_rule + * Field_position - Field position + * Field_length - Field length + * + * RETURN: Status + * + * DESCRIPTION: Construct an ACPI_OBJECT_INTERNAL of type Index_field and + * connect it to the parent NTE. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_prep_index_field_value ( + ACPI_NAMED_OBJECT *this_entry, + ACPI_HANDLE index_reg, + ACPI_HANDLE data_reg, + u8 field_flags, + u8 field_attribute, + u32 field_position, + u32 field_length) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status; + + + /* Parameter validation */ + + if (!index_reg || !data_reg) { + return (AE_AML_NO_OPERAND); + } + + /* Allocate a new object descriptor */ + + obj_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_INDEX_FIELD); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* Initialize areas of the object that are common to all fields */ + + status = acpi_aml_prep_common_field_object (obj_desc, field_flags, field_attribute, + field_position, field_length); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Initialize areas of the object that are specific to this field type */ + + obj_desc->index_field.value = (u32) (field_position / + obj_desc->field.granularity); + obj_desc->index_field.index = index_reg; + obj_desc->index_field.data = data_reg; + + /* Debug info */ + + /* + * Store the constructed descriptor (Obj_desc) into the nte whose + * handle is on TOS, preserving the current type of that nte. + */ + status = acpi_ns_attach_object ((ACPI_HANDLE) this_entry, obj_desc, + (u8) acpi_ns_get_type ((ACPI_HANDLE) this_entry)); + + return (status); +} + diff --git a/drivers/acpi/interpreter/amregion.c b/drivers/acpi/interpreter/amregion.c new file mode 100644 index 000000000..9a44daab2 --- /dev/null +++ b/drivers/acpi/interpreter/amregion.c @@ -0,0 +1,406 @@ +/****************************************************************************** + * + * Module Name: amregion - ACPI default Op_region (address space) handlers + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "hardware.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amregion"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_memory_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * Bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * Context - Context pointer + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System Memory address space (Op Region) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_memory_space_handler ( + u32 function, + u32 address, /* TBD: [Future] Should this be A POINTER for 64-bit support? */ + u32 bit_width, + u32 *value, + void *context) +{ + ACPI_STATUS status = AE_OK; + void *logical_addr_ptr = NULL; + MEM_HANDLER_CONTEXT *mem_info = context; + u32 length; + + + /* Validate and translate the bit width */ + + switch (bit_width) + { + case 8: + length = 1; + break; + + case 16: + length = 2; + break; + + case 32: + length = 4; + break; + + default: + return (AE_AML_OPERAND_VALUE); + break; + } + + + /* + * Does the request fit into the cached memory mapping? + * Is 1) Address below the current mapping? OR + * 2) Address beyond the current mapping? + */ + + if (((char *) address < mem_info->mapped_physical_address) || + (((char *) address + length) > + (mem_info->mapped_physical_address + mem_info->mapped_length))) + { + /* + * The request cannot be resolved by the current memory mapping; + * Delete the existing mapping and create a new one. + */ + + if (mem_info->mapped_length) { + /* Valid mapping, delete it */ + + acpi_os_unmap_memory (mem_info->mapped_logical_address, + mem_info->mapped_length); + } + + mem_info->mapped_length = 0; /* In case of failure below */ + + /* Create a new mapping starting at the address given */ + + status = acpi_os_map_memory ((void *) address, SYSMEM_REGION_WINDOW_SIZE, + (void **) &mem_info->mapped_logical_address); + if (ACPI_FAILURE (status)) { + return (status); + } + + mem_info->mapped_physical_address = (char *) address; + mem_info->mapped_length = SYSMEM_REGION_WINDOW_SIZE; + } + + + /* + * Generate a logical pointer corresponding to the address we want to + * access + */ + + logical_addr_ptr = mem_info->mapped_logical_address + + ((char *) address - mem_info->mapped_physical_address); + + /* Perform the memory read or write */ + + switch (function) + { + + case ADDRESS_SPACE_READ: + + switch (bit_width) + { + case 8: + *value = (u32)* (u8 *) logical_addr_ptr; + break; + + case 16: + MOVE_UNALIGNED16_TO_32 (value, logical_addr_ptr); + break; + + case 32: + MOVE_UNALIGNED32_TO_32 (value, logical_addr_ptr); + break; + } + + break; + + + case ADDRESS_SPACE_WRITE: + + switch (bit_width) + { + case 8: + *(u8 *) logical_addr_ptr = (u8) *value; + break; + + case 16: + MOVE_UNALIGNED16_TO_16 (logical_addr_ptr, value); + break; + + case 32: + MOVE_UNALIGNED32_TO_32 (logical_addr_ptr, value); + break; + } + + break; + + + default: + status = AE_BAD_PARAMETER; + break; + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_io_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * Bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * Context - Context pointer + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System IO address space (Op Region) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_io_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context) +{ + ACPI_STATUS status = AE_OK; + + + /* Decode the function parameter */ + + switch (function) + { + + case ADDRESS_SPACE_READ: + + switch (bit_width) + { + /* I/O Port width */ + + case 8: + *value = (u32) acpi_os_in8 ((ACPI_IO_ADDRESS) address); + break; + + case 16: + *value = (u32) acpi_os_in16 ((ACPI_IO_ADDRESS) address); + break; + + case 32: + *value = acpi_os_in32 ((ACPI_IO_ADDRESS) address); + break; + + default: + status = AE_AML_OPERAND_VALUE; + } + + break; + + + case ADDRESS_SPACE_WRITE: + + switch (bit_width) + { + /* I/O Port width */ + case 8: + acpi_os_out8 ((ACPI_IO_ADDRESS) address, (u8) *value); + break; + + case 16: + acpi_os_out16 ((ACPI_IO_ADDRESS) address, (u16) *value); + break; + + case 32: + acpi_os_out32 ((ACPI_IO_ADDRESS) address, *value); + break; + + default: + status = AE_AML_OPERAND_VALUE; + } + + break; + + + default: + status = AE_BAD_PARAMETER; + break; + } + + return (status); +} + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_pci_config_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * Bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * Context - Context pointer + * + * RETURN: Status + * + * DESCRIPTION: Handler for the PCI Config address space (Op Region) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_pci_config_space_handler ( + u32 function, + u32 address, + u32 bit_width, + u32 *value, + void *context) +{ + ACPI_STATUS status = AE_OK; + u32 pci_bus; + u32 dev_func; + u8 pci_reg; + PCI_HANDLER_CONTEXT *PCIcontext; + + + /* + * The arguments to Acpi_os(Read|Write)Pci_cfg(Byte|Word|Dword) are: + * + * Seg_bus - 0xSSSSBBBB - SSSS is the PCI bus segment + * BBBB is the PCI bus number + * + * Dev_func - 0xDDDDFFFF - DDDD is the PCI device number + * FFFF is the PCI device function number + * + * Reg_num - Config space register must be < 40h + * + * Value - input value for write, output for read + * + */ + + PCIcontext = (PCI_HANDLER_CONTEXT *) context; + + pci_bus = LOWORD(PCIcontext->seg) << 16; + pci_bus |= LOWORD(PCIcontext->bus); + + dev_func = PCIcontext->dev_func; + + pci_reg = (u8) address; + + switch (function) + { + + case ADDRESS_SPACE_READ: + + *value = 0; + + switch (bit_width) + { + /* PCI Register width */ + + case 8: + status = acpi_os_read_pci_cfg_byte (pci_bus, dev_func, pci_reg, + (u8 *) value); + break; + + case 16: + status = acpi_os_read_pci_cfg_word (pci_bus, dev_func, pci_reg, + (u16 *) value); + break; + + case 32: + status = acpi_os_read_pci_cfg_dword (pci_bus, dev_func, pci_reg, + value); + break; + + default: + status = AE_AML_OPERAND_VALUE; + + } /* Switch bit_width */ + + break; + + + case ADDRESS_SPACE_WRITE: + + + switch (bit_width) + { + /* PCI Register width */ + + case 8: + status = acpi_os_write_pci_cfg_byte (pci_bus, dev_func, pci_reg, + *(u8 *) value); + break; + + case 16: + status = acpi_os_write_pci_cfg_word (pci_bus, dev_func, pci_reg, + *(u16 *) value); + break; + + case 32: + status = acpi_os_write_pci_cfg_dword (pci_bus, dev_func, pci_reg, + *value); + break; + + default: + status = AE_AML_OPERAND_VALUE; + + } /* Switch bit_width */ + + break; + + + default: + + status = AE_BAD_PARAMETER; + break; + + } + + return (status); +} + diff --git a/drivers/acpi/interpreter/amresnte.c b/drivers/acpi/interpreter/amresnte.c new file mode 100644 index 000000000..1f8db8b3d --- /dev/null +++ b/drivers/acpi/interpreter/amresnte.c @@ -0,0 +1,555 @@ + +/****************************************************************************** + * + * Module Name: amresnte - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amresnte"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_resolve_entry_to_value + * + * PARAMETERS: Stack_ptr - Pointer to a location on a stack that contains + * a ptr to an NTE + * + * RETURN: Status + * + * DESCRIPTION: Resolve a ACPI_NAMED_OBJECT(nte, A.K.A. a "direct name pointer") + * + * Note: for some of the data types, the pointer attached to the NTE can be + * either a pointer to an actual internal object or a pointer into the AML + * stream itself. These types are currently: + * + * ACPI_TYPE_NUMBER + * ACPI_TYPE_STRING + * ACPI_TYPE_BUFFER + * ACPI_TYPE_MUTEX + * ACPI_TYPE_PACKAGE + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_resolve_entry_to_value ( + ACPI_NAMED_OBJECT **stack_ptr) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *val_desc = NULL; + ACPI_OBJECT_INTERNAL *obj_desc = NULL; + ACPI_NAMED_OBJECT *stack_entry; + u8 *aml_pointer = NULL; + OBJECT_TYPE_INTERNAL entry_type; + u8 locked; + u8 attached_aml_pointer = FALSE; + u8 aml_opcode = 0; + u32 temp_val; + OBJECT_TYPE_INTERNAL object_type; + + + stack_entry = *stack_ptr; + + + /* + * The stack pointer is a "Direct name ptr", and points to a + * a ACPI_NAMED_OBJECT(nte). Get the pointer that is attached to + * the nte. + */ + + val_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) stack_entry); + entry_type = acpi_ns_get_type ((ACPI_HANDLE) stack_entry); + + /* + * The Val_desc attached to the NTE can be either: + * 1) An internal ACPI object + * 2) A pointer into the AML stream (into one of the ACPI system tables) + */ + + if (acpi_tb_system_table_pointer (val_desc)) { + attached_aml_pointer = TRUE; + aml_opcode = *((u8 *) val_desc); + aml_pointer = ((u8 *) val_desc) + 1; + + } + + + /* + * Action is based on the type of the NTE, which indicates the type + * of the attached object or pointer + */ + switch (entry_type) + { + + case ACPI_TYPE_PACKAGE: + + /* + * Val_desc should point to either an ACPI_OBJECT_INTERNAL of + * type Package, or an initialization in the AML stream. + */ + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + + if (attached_aml_pointer) { + /* + * This means that the package initialization is not parsed + * -- should not happen + */ + return (AE_NOT_IMPLEMENTED); + } + + /* Val_desc is an internal object in all cases by the time we get here */ + + if (!val_desc || (ACPI_TYPE_PACKAGE != val_desc->common.type)) { + return (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = val_desc; + acpi_cm_add_reference (obj_desc); + break; + + + case ACPI_TYPE_BUFFER: + + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + if (attached_aml_pointer) { + /* + * This means that the buffer initialization is not parsed + * -- should not happen + */ + return (AE_NOT_IMPLEMENTED); + } + + /* Val_desc is an internal object in all cases by the time we get here */ + + if (!val_desc || (ACPI_TYPE_BUFFER != val_desc->common.type)) { + return (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = val_desc; + acpi_cm_add_reference (obj_desc); + + break; + + + case ACPI_TYPE_STRING: + + if (attached_aml_pointer) { + /* Allocate a new string object */ + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_STRING); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* Init the internal object */ + + obj_desc->string.pointer = (char *) aml_pointer; + obj_desc->string.length = STRLEN (aml_pointer); + } + + else { + if (ACPI_TYPE_STRING != val_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = val_desc; + acpi_cm_add_reference (obj_desc); + } + + break; + + + case ACPI_TYPE_NUMBER: + + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + + /* + * An ACPI_TYPE_NUMBER can be either an object or an AML pointer + */ + + if (attached_aml_pointer) { + /* + * The attachment points into the AML stream, get the number from + * there. The actual number is based upon the AML opcode + * + * Note: Word_op and DWord_op will not work properly if the + * processor's endianness does not match the AML's. + */ + + switch (aml_opcode) + { + + case AML_ZERO_OP: + + temp_val = 0; + break; + + + case AML_ONE_OP: + + temp_val = 1; + break; + + + case AML_ONES_OP: + + temp_val = 0xFFFFFFFF; + break; + + + case AML_BYTE_OP: + + temp_val = (u32) ((u8 *) val_desc)[1]; + break; + + + case AML_WORD_OP: + + MOVE_UNALIGNED16_TO_32 (&temp_val, &((u8 *) val_desc)[1]); + break; + + + case AML_DWORD_OP: + + MOVE_UNALIGNED32_TO_32 (&temp_val, &((u8 *) val_desc)[1]); + break; + + + default: + + return (AE_AML_BAD_OPCODE); + + } /* switch */ + + + /* Create and initialize a new object */ + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + obj_desc->number.value = temp_val; + } + + else { + /* + * The NTE has an attached internal object, make sure that it's a + * number + */ + + if (ACPI_TYPE_NUMBER != val_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = val_desc; + acpi_cm_add_reference (obj_desc); + } + + break; + + + case INTERNAL_TYPE_DEF_FIELD: + + /* + * TBD: [Investigate] Is this the correct solution? + * + * This section was extended to convert to generic buffer if + * the return length is greater than 32 bits, but still allows + * for returning a type Number for smaller values because the + * caller can then apply arithmetic operators on those fields. + * + * XXX - Implementation limitation: Fields are implemented as type + * XXX - Number, but they really are supposed to be type Buffer. + * XXX - The two are interchangeable only for lengths <= 32 bits. + */ + if(val_desc->field.length > 32) { + object_type = ACPI_TYPE_BUFFER; + } + else { + object_type = ACPI_TYPE_NUMBER; + } + + /* + * Create the destination buffer object and the buffer space. + */ + obj_desc = acpi_cm_create_internal_object (object_type); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + /* + * Fill in the object specific details + */ + if (ACPI_TYPE_BUFFER == object_type) { + obj_desc->buffer.pointer = acpi_cm_callocate(val_desc->field.length); + if (!obj_desc->buffer.pointer) { + acpi_cm_remove_reference(obj_desc); + return (AE_NO_MEMORY); + } + + obj_desc->buffer.length = val_desc->field.length; + + status = acpi_aml_get_named_field_value ((ACPI_HANDLE) stack_entry, + obj_desc->buffer.pointer, obj_desc->buffer.length); + + if (AE_OK != status) { + return (status); + } + } + else { + status = acpi_aml_get_named_field_value ((ACPI_HANDLE) stack_entry, + &temp_val, sizeof (temp_val)); + + if (AE_OK != status) { + return (status); + } + + obj_desc->number.value = temp_val; + } + + + break; + + + case INTERNAL_TYPE_BANK_FIELD: + + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + if (attached_aml_pointer) { + return (AE_AML_OPERAND_TYPE); + } + + if (INTERNAL_TYPE_BANK_FIELD != val_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + + /* Get the global lock if needed */ + + obj_desc = (ACPI_OBJECT_INTERNAL *) *stack_ptr; + locked = acpi_aml_acquire_global_lock (obj_desc->field_unit.lock_rule); + { + + /* Set Index value to select proper Data register */ + /* perform the update */ + + status = acpi_aml_set_named_field_value (val_desc->bank_field.bank_select, + &val_desc->bank_field.value, + sizeof (val_desc->bank_field.value)); + } + acpi_aml_release_global_lock (locked); + + + if (AE_OK != status) { + return (status); + } + + /* Read Data value */ + + status = acpi_aml_get_named_field_value ( + (ACPI_HANDLE) val_desc->bank_field.container, + &temp_val, sizeof (temp_val)); + if (AE_OK != status) { + return (status); + } + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + obj_desc->number.value = temp_val; + break; + + + case INTERNAL_TYPE_INDEX_FIELD: + + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + if (attached_aml_pointer) { + return (AE_AML_OPERAND_TYPE); + } + + if (INTERNAL_TYPE_INDEX_FIELD != val_desc->common.type) { + return (AE_AML_OPERAND_TYPE); + } + + + /* Set Index value to select proper Data register */ + /* Get the global lock if needed */ + + obj_desc = (ACPI_OBJECT_INTERNAL *) *stack_ptr; + locked = acpi_aml_acquire_global_lock (obj_desc->field_unit.lock_rule); + { + /* Perform the update */ + + status = acpi_aml_set_named_field_value (val_desc->index_field.index, + &val_desc->index_field.value, + sizeof (val_desc->index_field.value)); + } + acpi_aml_release_global_lock (locked); + + if (AE_OK != status) { + return (status); + } + + /* Read Data value */ + + status = acpi_aml_get_named_field_value (val_desc->index_field.data, &temp_val, sizeof (temp_val)); + if (AE_OK != status) { + return (status); + } + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_NUMBER); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + obj_desc->number.value = temp_val; + break; + + + case ACPI_TYPE_FIELD_UNIT: + + if (!val_desc) { + return (AE_AML_NO_OPERAND); + } + + if (attached_aml_pointer) { + return (AE_AML_OPERAND_TYPE); + } + + if (val_desc->common.type != (u8) entry_type) { + return (AE_AML_OPERAND_TYPE); + break; + } + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_ANY); + if (!obj_desc) { + return (AE_NO_MEMORY); + } + + if ((status = acpi_aml_get_field_unit_value (val_desc, obj_desc)) != AE_OK) { + acpi_cm_remove_reference (obj_desc); + return (status); + } + + break; + + + /* + * For these objects, just return the object attached to the NTE + */ + + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_METHOD: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_REGION: + + + /* There must be an object attached to this NTE */ + + if (!val_desc) { + return (AE_AML_INTERNAL); + } + + /* Return an additional reference to the object */ + + obj_desc = val_desc; + acpi_cm_add_reference (obj_desc); + break; + + + /* Devices rarely have an attached object, return the NTE */ + + case ACPI_TYPE_DEVICE: + + + /* Method locals and arguments have a pseudo-NTE, just return it */ + + case INTERNAL_TYPE_METHOD_ARGUMENT: + case INTERNAL_TYPE_METHOD_LOCAL_VAR: + + return (AE_OK); + break; + + + /* TYPE_Any is untyped, and thus there is no object associated with it */ + + case ACPI_TYPE_ANY: + + return (AE_AML_OPERAND_TYPE); /* Cannot be AE_TYPE */ + + + /* Default case is for unknown types */ + + default: + + return (AE_AML_OPERAND_TYPE); + + } /* switch (Entry_type) */ + + + /* Put the object descriptor on the stack */ + + *stack_ptr = (void *) obj_desc; + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amresolv.c b/drivers/acpi/interpreter/amresolv.c new file mode 100644 index 000000000..886ebb7fc --- /dev/null +++ b/drivers/acpi/interpreter/amresolv.c @@ -0,0 +1,452 @@ + +/****************************************************************************** + * + * Module Name: amresolv - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amresolv"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_get_field_unit_value + * + * PARAMETERS: *Field_desc - Pointer to a Field_unit + * *Result_desc - Pointer to an empty descriptor + * which will become a Number + * containing the field's value. + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value from a Field_unit + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_get_field_unit_value ( + ACPI_OBJECT_INTERNAL *field_desc, + ACPI_OBJECT_INTERNAL *result_desc) +{ + ACPI_STATUS status = AE_OK; + u32 mask; + u8 *location = NULL; + u8 locked = FALSE; + + + if (!field_desc) { + status = AE_AML_NO_OPERAND; + } + + else if (!field_desc->field_unit.container) { + status = AE_AML_INTERNAL; + } + + else if (ACPI_TYPE_BUFFER != field_desc->field_unit.container->common.type) { + status = AE_AML_OPERAND_TYPE; + } + + else if (field_desc->field_unit.sequence + != field_desc->field_unit.container->buffer.sequence) + { + status = AE_AML_INTERNAL; + } + + else if (!result_desc) { + status = AE_AML_INTERNAL; + } + + if (ACPI_FAILURE (status)) { + return (status); + } + + + /* Get the global lock if needed */ + + locked = acpi_aml_acquire_global_lock (field_desc->field_unit.lock_rule); + + /* Field location is (base of buffer) + (byte offset) */ + + location = field_desc->field_unit.container->buffer.pointer + + field_desc->field_unit.offset; + + /* + * Construct Mask with as many 1 bits as the field width + * + * NOTE: Only the bottom 5 bits are valid for a shift operation, so + * special care must be taken for any shift greater than 31 bits. + * + * TBD: [Unhandled] Fields greater than 32-bits will not work. + */ + + if (field_desc->field_unit.length < 32) { + mask = ((u32) 1 << field_desc->field_unit.length) - (u32) 1; + } + else { + mask = 0xFFFFFFFF; + } + + result_desc->number.type = (u8) ACPI_TYPE_NUMBER; + + /* Get the 32 bit value at the location */ + + MOVE_UNALIGNED32_TO_32 (&result_desc->number.value, location); + + /* + * Shift the 32-bit word containing the field, and mask off the + * resulting value + */ + + result_desc->number.value = + (result_desc->number.value >> field_desc->field_unit.bit_offset) & mask; + + /* Release global lock if we acquired it earlier */ + + acpi_aml_release_global_lock (locked); + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_resolve_to_value + * + * PARAMETERS: **Stack_ptr - Points to entry on Obj_stack, which can + * be either an (ACPI_OBJECT_INTERNAL *) + * or an ACPI_HANDLE. + * + * RETURN: Status + * + * DESCRIPTION: Convert Reference entries on Obj_stack to Rvalues + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_resolve_to_value ( + ACPI_OBJECT_INTERNAL **stack_ptr) +{ + ACPI_STATUS status = AE_OK; + + + if (!stack_ptr || !*stack_ptr) { + return (AE_AML_NO_OPERAND); + } + + + /* + * The entity pointed to by the Stack_ptr can be either + * 1) A valid ACPI_OBJECT_INTERNAL, or + * 2) A ACPI_NAMED_OBJECT(nte) + */ + + if (VALID_DESCRIPTOR_TYPE (*stack_ptr, ACPI_DESC_TYPE_INTERNAL)) { + + status = acpi_aml_resolve_object_to_value (stack_ptr); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + /* + * Object on the stack may have changed if Acpi_aml_resolve_object_to_value() + * was called (i.e., we can't use an _else_ here.) + */ + + if (VALID_DESCRIPTOR_TYPE (*stack_ptr, ACPI_DESC_TYPE_NAMED)) { + status = acpi_aml_resolve_entry_to_value ((ACPI_NAMED_OBJECT**) stack_ptr); + } + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_resolve_object_to_value + * + * PARAMETERS: Stack_ptr - Pointer to a stack location that contains a + * ptr to an internal object. + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value from an internal object. The Reference type + * uses the associated AML opcode to determine the value. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_resolve_object_to_value ( + ACPI_OBJECT_INTERNAL **stack_ptr) +{ + ACPI_OBJECT_INTERNAL *stack_desc; + ACPI_STATUS status = AE_OK; + ACPI_HANDLE temp_handle = NULL; + ACPI_OBJECT_INTERNAL *obj_desc = NULL; + u32 index = 0; + u16 opcode; + + + stack_desc = *stack_ptr; + + /* This is an ACPI_OBJECT_INTERNAL */ + + switch (stack_desc->common.type) + { + + case INTERNAL_TYPE_REFERENCE: + + opcode = stack_desc->reference.op_code; + + switch (opcode) + { + + case AML_NAME_OP: + + /* + * Convert indirect name ptr to a direct name ptr. + * Then, Acpi_aml_resolve_entry_to_value can be used to get the value + */ + + temp_handle = stack_desc->reference.object; + + /* Delete the Reference Object */ + + acpi_cm_remove_reference (stack_desc); + + /* Put direct name pointer onto stack and exit */ + + (*stack_ptr) = temp_handle; + status = AE_OK; + break; + + + case AML_LOCAL_OP: + + index = stack_desc->reference.offset; + + /* Delete the Reference Object */ + + acpi_cm_remove_reference (stack_desc); + + /* + * Get the local from the method's state info + * Note: this increments the object reference count + */ + + status = acpi_ds_method_data_get_value (MTH_TYPE_LOCAL, index, + stack_ptr); + if (ACPI_FAILURE (status)) { + return (status); + } + + stack_desc = *stack_ptr; + + if (ACPI_TYPE_NUMBER == stack_desc->common.type) { + /* Value is a Number */ + + } + + break; + + + case AML_ARG_OP: + + index = stack_desc->reference.offset; + + /* Delete the Reference Object*/ + + acpi_cm_remove_reference (stack_desc); + + /* + * Get the argument from the method's state info + * Note: this increments the object reference count + */ + + status = acpi_ds_method_data_get_value (MTH_TYPE_ARG, index, + stack_ptr); + if (ACPI_FAILURE (status)) { + return (status); + } + + stack_desc = *stack_ptr; + + if (ACPI_TYPE_NUMBER == stack_desc->common.type) { + /* Value is a Number */ + + } + + break; + + + /* + * TBD: [Restructure] These next three opcodes change the type of + * the object, which is actually a no-no. + */ + + case AML_ZERO_OP: + + stack_desc->common.type = (u8) ACPI_TYPE_NUMBER; + stack_desc->number.value = 0; + break; + + + case AML_ONE_OP: + + stack_desc->common.type = (u8) ACPI_TYPE_NUMBER; + stack_desc->number.value = 1; + break; + + + case AML_ONES_OP: + + stack_desc->common.type = (u8) ACPI_TYPE_NUMBER; + stack_desc->number.value = 0xFFFFFFFF; + break; + + + case AML_INDEX_OP: + + switch (stack_desc->reference.target_type) + { + case ACPI_TYPE_BUFFER_FIELD: + + /* Just return - leave the Reference on the stack */ + break; + + + case ACPI_TYPE_PACKAGE: + obj_desc = *stack_desc->reference.where; + if (obj_desc) { + /* + * Valid obj descriptor, copy pointer to return value + * (i.e., dereference the package index) + * Delete the ref object, increment the returned object + */ + acpi_cm_remove_reference (stack_desc); + acpi_cm_add_reference (obj_desc); + *stack_ptr = obj_desc; + } + + else { + /* + * A NULL object descriptor means an unitialized element of + * the package, can't deref it + */ + + status = AE_AML_UNINITIALIZED_ELEMENT; + } + break; + + default: + /* Invalid reference OBJ*/ + + status = AE_AML_INTERNAL; + break; + } + + break; + + + case AML_DEBUG_OP: + + /* Just leave the object as-is */ + break; + + + default: + + status = AE_AML_INTERNAL; + + } /* switch (Opcode) */ + + + if (AE_OK != status) { + return (status); + } + + break; /* case INTERNAL_TYPE_REFERENCE */ + + + case ACPI_TYPE_FIELD_UNIT: + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_ANY); + if (!obj_desc) { + /* Descriptor allocation failure */ + + return (AE_NO_MEMORY); + } + + status = acpi_aml_get_field_unit_value (stack_desc, obj_desc); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + obj_desc = NULL; + } + + *stack_ptr = (void *) obj_desc; + break; + + + case INTERNAL_TYPE_BANK_FIELD: + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_ANY); + if (!obj_desc) { + /* Descriptor allocation failure */ + + return (AE_NO_MEMORY); + } + + status = acpi_aml_get_field_unit_value (stack_desc, obj_desc); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (obj_desc); + obj_desc = NULL; + } + + *stack_ptr = (void *) obj_desc; + break; + + + /* TBD: [Future] - may need to handle Index_field, and Def_field someday */ + + default: + + break; + + } /* switch (Stack_desc->Common.Type) */ + + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amresop.c b/drivers/acpi/interpreter/amresop.c new file mode 100644 index 000000000..a579d94ca --- /dev/null +++ b/drivers/acpi/interpreter/amresop.c @@ -0,0 +1,429 @@ + +/****************************************************************************** + * + * Module Name: amresop - AML Interpreter operand/object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "namesp.h" +#include "tables.h" +#include "events.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amresop"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_resolve_operands + * + * PARAMETERS: Opcode Opcode being interpreted + * Stack_ptr Top of operand stack + * + * RETURN: Status + * + * DESCRIPTION: Convert stack entries to required types + * + * Each nibble in Arg_types represents one required operand + * and indicates the required Type: + * + * The corresponding stack entry will be converted to the + * required type if possible, else return an exception + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_resolve_operands ( + u16 opcode, + ACPI_OBJECT_INTERNAL **stack_ptr) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_STATUS status = AE_OK; + u8 object_type; + ACPI_HANDLE temp_handle; + u32 arg_types; + ACPI_OP_INFO *op_info; + u32 this_arg_type; + + + op_info = acpi_ps_get_opcode_info (opcode); + if (!op_info) { + return (AE_AML_BAD_OPCODE); + } + + + arg_types = op_info->runtime_args; + if (arg_types == ARGI_INVALID_OPCODE) { + status = AE_AML_INTERNAL; + goto cleanup; + } + + + /* + * Normal exit is with *Types == '\0' at end of string. + * Function will return an exception from within the loop upon + * finding an entry which is not, and cannot be converted + * to, the required type; if stack underflows; or upon + * finding a NULL stack entry (which "should never happen"). + */ + + while (GET_CURRENT_ARG_TYPE (arg_types)) { + if (!stack_ptr || !*stack_ptr) { + status = AE_AML_INTERNAL; + goto cleanup; + } + + /* Extract useful items */ + + obj_desc = *stack_ptr; + + /* Decode the descriptor type */ + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) { + /* NTE */ + + object_type = ((ACPI_NAMED_OBJECT*) obj_desc)->type; + } + + else if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_INTERNAL)) { + /* ACPI internal object */ + + object_type = obj_desc->common.type; + + /* Check for bad ACPI_OBJECT_TYPE */ + + if (!acpi_aml_validate_object_type (object_type)) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + if (object_type == (u8) INTERNAL_TYPE_REFERENCE) { + /* + * Decode the Reference + */ + + op_info = acpi_ps_get_opcode_info (opcode); + if (!op_info) { + return (AE_AML_BAD_OPCODE); + } + + + switch (obj_desc->reference.op_code) + { + case AML_ZERO_OP: + case AML_ONE_OP: + case AML_ONES_OP: + case AML_DEBUG_OP: + case AML_NAME_OP: + case AML_INDEX_OP: + case AML_ARG_OP: + case AML_LOCAL_OP: + + break; + + default: + status = AE_AML_OPERAND_TYPE; + goto cleanup; + break; + } + } + + } + + else { + /* Invalid descriptor */ + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + + /* + * Decode a character from the type string + */ + + this_arg_type = GET_CURRENT_ARG_TYPE (arg_types); + INCREMENT_ARG_LIST (arg_types); + + + switch (this_arg_type) + { + + case ARGI_REFERENCE: /* Reference */ + case ARGI_TARGETREF: + + /* Need an operand of type INTERNAL_TYPE_REFERENCE */ + + if (VALID_DESCRIPTOR_TYPE (obj_desc, ACPI_DESC_TYPE_NAMED)) /* direct name ptr OK as-is */ { + break; + } + + if (INTERNAL_TYPE_REFERENCE != object_type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + if (AML_NAME_OP == obj_desc->reference.op_code) { + /* + * Convert an indirect name ptr to direct name ptr and put + * it on the stack + */ + + temp_handle = obj_desc->reference.object; + acpi_cm_remove_reference (obj_desc); + (*stack_ptr) = temp_handle; + } + break; + + + case ARGI_NUMBER: /* Number */ + + /* Need an operand of type ACPI_TYPE_NUMBER */ + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_NUMBER != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_STRING: + + /* Need an operand of type ACPI_TYPE_STRING or ACPI_TYPE_BUFFER */ + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + if ((ACPI_TYPE_STRING != (*stack_ptr)->common.type) && + (ACPI_TYPE_BUFFER != (*stack_ptr)->common.type)) + { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_BUFFER: + + /* Need an operand of type ACPI_TYPE_BUFFER */ + + if ((status = acpi_aml_resolve_to_value(stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_BUFFER != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_MUTEX: + + /* Need an operand of type ACPI_TYPE_MUTEX */ + + if ((status = acpi_aml_resolve_to_value(stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_MUTEX != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_EVENT: + + /* Need an operand of type ACPI_TYPE_EVENT */ + + if ((status = acpi_aml_resolve_to_value(stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_EVENT != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_REGION: + + /* Need an operand of type ACPI_TYPE_REGION */ + + if ((status = acpi_aml_resolve_to_value(stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_REGION != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_IF: /* If */ + + /* Need an operand of type INTERNAL_TYPE_IF */ + + if (INTERNAL_TYPE_IF != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_PACKAGE: /* Package */ + + /* Need an operand of type ACPI_TYPE_PACKAGE */ + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + if (ACPI_TYPE_PACKAGE != (*stack_ptr)->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case ARGI_ANYTYPE: + + + /* + * We don't want to resolve Index_op reference objects during + * a store because this would be an implicit De_ref_of operation. + * Instead, we just want to store the reference object. + */ + + if ((opcode == AML_STORE_OP) && + ((*stack_ptr)->common.type == INTERNAL_TYPE_REFERENCE) && + ((*stack_ptr)->reference.op_code == AML_INDEX_OP)) + { + break; + } + + /* All others must be resolved */ + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + /* All types OK, so we don't perform any typechecks */ + + break; + + + case ARGI_DATAOBJECT: + /* + * ARGI_DATAOBJECT is only used by the Size_of operator. + * + * The ACPI specification allows Size_of to return the size of + * a Buffer, String or Package. However, the MS ACPI.SYS AML + * Interpreter also allows an NTE reference to return without + * error with a size of 4. + */ + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + /* Need a buffer, string, package or NTE reference */ + + if (((*stack_ptr)->common.type != ACPI_TYPE_BUFFER) && + ((*stack_ptr)->common.type != ACPI_TYPE_STRING) && + ((*stack_ptr)->common.type != ACPI_TYPE_PACKAGE) && + ((*stack_ptr)->common.type != INTERNAL_TYPE_REFERENCE)) + { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * If this is a reference, only allow a reference to an NTE. + */ + if ((*stack_ptr)->common.type == INTERNAL_TYPE_REFERENCE) { + if (!(*stack_ptr)->reference.nte) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } + + break; + + + case ARGI_COMPLEXOBJ: + + if ((status = acpi_aml_resolve_to_value (stack_ptr)) != AE_OK) { + goto cleanup; + } + + /* Need a buffer or package */ + + if (((*stack_ptr)->common.type != ACPI_TYPE_BUFFER) && + ((*stack_ptr)->common.type != ACPI_TYPE_PACKAGE)) + { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + /* Unknown abbreviation passed in */ + + default: + status = AE_BAD_PARAMETER; + goto cleanup; + + } /* switch (*Types++) */ + + + /* + * If more operands needed, decrement Stack_ptr to point + * to next operand on stack (after checking for underflow). + */ + if (GET_CURRENT_ARG_TYPE (arg_types)) { + stack_ptr--; + } + + } /* while (*Types) */ + + +cleanup: + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amstore.c b/drivers/acpi/interpreter/amstore.c new file mode 100644 index 000000000..9f9ec96d8 --- /dev/null +++ b/drivers/acpi/interpreter/amstore.c @@ -0,0 +1,374 @@ + +/****************************************************************************** + * + * Module Name: amstore - AML Interpreter object store support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "tables.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amstore"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exec_store + * + * PARAMETERS: *Val_desc - Value to be stored + * *Dest_desc - Where to store it 0 Must be (ACPI_HANDLE) + * or an ACPI_OBJECT_INTERNAL of type + * Reference; if the latter the descriptor + * will be either reused or deleted. + * + * RETURN: Status + * + * DESCRIPTION: Store the value described by Val_desc into the location + * described by Dest_desc. Called by various interpreter + * functions to store the result of an operation into + * the destination operand. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_exec_store ( + ACPI_OBJECT_INTERNAL *val_desc, + ACPI_OBJECT_INTERNAL *dest_desc) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *delete_dest_desc = NULL; + ACPI_OBJECT_INTERNAL *tmp_desc; + ACPI_NAMED_OBJECT *entry = NULL; + u8 value = 0; + u32 length; + u32 i; + + + /* Validate parameters */ + + if (!val_desc || !dest_desc) { + return (AE_AML_NO_OPERAND); + } + + /* Examine the datatype of the Dest_desc */ + + if (VALID_DESCRIPTOR_TYPE (dest_desc, ACPI_DESC_TYPE_NAMED)) { + /* Dest is an ACPI_HANDLE, create a new object */ + + entry = (ACPI_NAMED_OBJECT*) dest_desc; + dest_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); + if (!dest_desc) { + /* Allocation failure */ + + return (AE_NO_MEMORY); + } + + /* Build a new Reference wrapper around the handle */ + + dest_desc->reference.op_code = AML_NAME_OP; + dest_desc->reference.object = entry; + } + + + /* Destination object must be of type Reference */ + + if (dest_desc->common.type != INTERNAL_TYPE_REFERENCE) { + /* Destination is not an Reference */ + + return (AE_AML_OPERAND_TYPE); + } + + /* Examine the Reference opcode */ + + switch (dest_desc->reference.op_code) + { + + case AML_NAME_OP: + + /* + * Storing into a Name + */ + delete_dest_desc = dest_desc; + status = acpi_aml_store_object_to_nte (val_desc, dest_desc->reference.object); + + break; /* Case Name_op */ + + + case AML_INDEX_OP: + + delete_dest_desc = dest_desc; + + /* + * Valid source value and destination reference pointer. + * + * ACPI Specification 1.0B section 15.2.3.4.2.13: + * Destination should point to either a buffer or a package + */ + + /* + * Actually, storing to a package is not so simple. The source must be + * evaluated and converted to the type of the destination and then the + * source is copied into the destination - we can't just point to the + * source object. + */ + if (dest_desc->reference.target_type == ACPI_TYPE_PACKAGE) { + /* + * The object at *(Dest_desc->Reference.Where) is the + * element within the package that is to be modified. + */ + tmp_desc = *(dest_desc->reference.where); + if (tmp_desc) { + /* + * If the Destination element is a package, we will delete + * that object and construct a new one. + * + * TBD: [Investigate] Should both the src and dest be required + * to be packages? + * && (Val_desc->Common.Type == ACPI_TYPE_PACKAGE) + */ + if (tmp_desc->common.type == ACPI_TYPE_PACKAGE) { + /* + * Take away the reference for being part of a package and + * delete + */ + acpi_cm_remove_reference (tmp_desc); + acpi_cm_remove_reference (tmp_desc); + + tmp_desc = NULL; + } + } + + if (!tmp_desc) { + /* + * If the Tmp_desc is NULL, that means an uninitialized package + * has been used as a destination, therefore, we must create + * the destination element to match the type of the source + * element NOTE: Val_desc can be of any type. + */ + tmp_desc = acpi_cm_create_internal_object (val_desc->common.type); + if (!tmp_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * If the source is a package, copy the source to the new dest + */ + if (ACPI_TYPE_PACKAGE == tmp_desc->common.type) { + status = acpi_aml_build_copy_internal_package_object ( + val_desc, tmp_desc); + if (ACPI_FAILURE (status)) { + acpi_cm_remove_reference (tmp_desc); + tmp_desc = NULL; + goto cleanup; + } + } + + /* + * Install the new descriptor into the package and add a + * reference to the newly created descriptor for now being + * part of the parent package + */ + + *(dest_desc->reference.where) = tmp_desc; + acpi_cm_add_reference (tmp_desc); + } + + if (ACPI_TYPE_PACKAGE != tmp_desc->common.type) { + /* + * The destination element is not a package, so we need to + * convert the contents of the source (Val_desc) and copy into + * the destination (Tmp_desc) + */ + status = acpi_aml_store_object_to_object(val_desc, tmp_desc); + if (ACPI_FAILURE (status)) { + /* + * An error occurrered when copying the internal object + * so delete the reference. + */ + status = AE_AML_OPERAND_TYPE; + } + } + + break; + } + + /* + * Check that the destination is a Buffer Field type + */ + if (dest_desc->reference.target_type != ACPI_TYPE_BUFFER_FIELD) { + status = AE_AML_OPERAND_TYPE; + break; + } + + /* + * Storing into a buffer at a location defined by an Index. + * + * Each 8-bit element of the source object is written to the + * 8-bit Buffer Field of the Index destination object. + */ + + /* + * Set the Tmp_desc to the destination object and type check. + */ + tmp_desc = dest_desc->reference.object; + + if (tmp_desc->common.type != ACPI_TYPE_BUFFER) { + status = AE_AML_OPERAND_TYPE; + break; + } + + /* + * The assignment of the individual elements will be slightly + * different for each source type. + */ + + switch (val_desc->common.type) + { + /* + * If the type is Integer, the Length is 4. + * This loop to assign each of the elements is somewhat + * backward because of the Big Endian-ness of IA-64 + */ + case ACPI_TYPE_NUMBER: + length = 4; + for (i = length; i != 0; i--) { + value = (u8)(val_desc->number.value >> (MUL_8 (i - 1))); + tmp_desc->buffer.pointer[dest_desc->reference.offset] = value; + } + break; + + /* + * If the type is Buffer, the Length is in the structure. + * Just loop through the elements and assign each one in turn. + */ + case ACPI_TYPE_BUFFER: + length = val_desc->buffer.length; + for (i = 0; i < length; i++) { + value = *(val_desc->buffer.pointer + i); + tmp_desc->buffer.pointer[dest_desc->reference.offset] = value; + } + break; + + /* + * If the type is String, the Length is in the structure. + * Just loop through the elements and assign each one in turn. + */ + case ACPI_TYPE_STRING: + length = val_desc->string.length; + for (i = 0; i < length; i++) { + value = *(val_desc->string.pointer + i); + tmp_desc->buffer.pointer[dest_desc->reference.offset] = value; + } + break; + + /* + * If source is not a valid type so return an error. + */ + default: + status = AE_AML_OPERAND_TYPE; + break; + } + + /* + * If we had an error, break out of this case statement. + */ + if(AE_OK != status) { + break; + } + + /* + * Set the return pointer + */ + dest_desc = tmp_desc; + + break; + + case AML_ZERO_OP: + case AML_ONE_OP: + case AML_ONES_OP: + + /* + * Storing to a constant is a no-op -- see spec sec 15.2.3.3.1. + * Delete the result descriptor. + */ + + delete_dest_desc = dest_desc; + break; + + + case AML_LOCAL_OP: + + status = acpi_ds_method_data_set_value (MTH_TYPE_LOCAL, + (dest_desc->reference.offset), val_desc); + delete_dest_desc = dest_desc; + break; + + + case AML_ARG_OP: + + status = acpi_ds_method_data_set_value (MTH_TYPE_ARG, + (dest_desc->reference.offset), val_desc); + delete_dest_desc = dest_desc; + break; + + + case AML_DEBUG_OP: + + /* + * Storing to the Debug object causes the value stored to be + * displayed and otherwise has no effect -- see sec. 15.2.3.3.3. + */ + + delete_dest_desc = dest_desc; + break; + + + default: + + /* TBD: [Restructure] use object dump routine !! */ + + delete_dest_desc = dest_desc; + status = AE_AML_INTERNAL; + + } /* switch(Dest_desc->Reference.Op_code) */ + + +cleanup: + + /* Cleanup and exit*/ + + if (delete_dest_desc) { + acpi_cm_remove_reference (delete_dest_desc); + } + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amstoren.c b/drivers/acpi/interpreter/amstoren.c new file mode 100644 index 000000000..4d35d0c54 --- /dev/null +++ b/drivers/acpi/interpreter/amstoren.c @@ -0,0 +1,518 @@ + +/****************************************************************************** + * + * Module Name: amstoren - AML Interpreter object store support, store to NTE + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "tables.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amstoren"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_store_object_to_nte + * + * PARAMETERS: *Val_desc - Value to be stored + * *Entry - Named object to recieve the value + * + * RETURN: Status + * + * DESCRIPTION: Store the object to the named object. + * + * The Assignment of an object to a named object is handled here + * The val passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. + * + * NOTE: the global lock is acquired early. This will result + * in the global lock being held a bit longer. Also, if the + * function fails during set up we may get the lock when we + * don't really need it. I don't think we care. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_store_object_to_nte ( + ACPI_OBJECT_INTERNAL *val_desc, + ACPI_NAMED_OBJECT *entry) +{ + ACPI_STATUS status = AE_OK; + u8 *buffer = NULL; + u32 length = 0; + u32 mask; + u32 new_value; + u8 locked = FALSE; + u8 *location=NULL; + ACPI_OBJECT_INTERNAL *dest_desc; + OBJECT_TYPE_INTERNAL destination_type = ACPI_TYPE_ANY; + + + /* + * Assuming the parameters are valid!!! + */ + ACPI_ASSERT((entry) && (val_desc)); + + destination_type = acpi_ns_get_type (entry); + + /* + * First ensure we have a value that can be stored in the target + */ + switch (destination_type) + { + /* Type of Name's existing value */ + + case INTERNAL_TYPE_ALIAS: + + /* + * Aliases are resolved by Acpi_aml_prep_operands + */ + + status = AE_AML_INTERNAL; + break; + + + case INTERNAL_TYPE_BANK_FIELD: + case INTERNAL_TYPE_INDEX_FIELD: + case ACPI_TYPE_FIELD_UNIT: + case ACPI_TYPE_NUMBER: + + /* + * These cases all require only number values or values that + * can be converted to numbers. + * + * If value is not a Number, try to resolve it to one. + */ + + if (val_desc->common.type != ACPI_TYPE_NUMBER) { + /* + * Initially not a number, convert + */ + status = acpi_aml_resolve_to_value (&val_desc); + if ((status == AE_OK) && + (val_desc->common.type != ACPI_TYPE_NUMBER)) + { + /* + * Conversion successful but still not a number + */ + status = AE_AML_OPERAND_TYPE; + } + } + + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case INTERNAL_TYPE_DEF_FIELD: + + /* + * Storing into a Field in a region or into a buffer or into + * a string all is essentially the same. + * + * If value is not a valid type, try to resolve it to one. + */ + + if ((val_desc->common.type != ACPI_TYPE_NUMBER) && + (val_desc->common.type != ACPI_TYPE_BUFFER) && + (val_desc->common.type != ACPI_TYPE_STRING)) + { + /* + * Initially not a valid type, convert + */ + status = acpi_aml_resolve_to_value (&val_desc); + if ((status == AE_OK) && + (val_desc->common.type != ACPI_TYPE_NUMBER) && + (val_desc->common.type != ACPI_TYPE_BUFFER) && + (val_desc->common.type != ACPI_TYPE_STRING)) + { + /* + * Conversion successful but still not a valid type + */ + status = AE_AML_OPERAND_TYPE; + } + } + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * TBD: [Unhandled] Not real sure what to do here + */ + status = AE_NOT_IMPLEMENTED; + break; + + + default: + + /* + * All other types than Alias and the various Fields come here. + * Store Val_desc as the new value of the Name, and set + * the Name's type to that of the value being stored in it. + * Val_desc reference count is incremented by Attach_object. + */ + + status = acpi_ns_attach_object (entry, val_desc, val_desc->common.type); + + goto clean_up_and_bail_out; + break; + } + + /* Exit now if failure above */ + + if (status != AE_OK) { + goto clean_up_and_bail_out; + } + + /* + * Get descriptor for object attached to NTE + */ + dest_desc = acpi_ns_get_attached_object (entry); + if (!dest_desc) { + /* + * There is no existing object attached to this NTE + */ + status = AE_AML_INTERNAL; + goto clean_up_and_bail_out; + } + + /* + * Make sure the destination Object is the same as the NTE + */ + if (dest_desc->common.type != (u8) destination_type) { + status = AE_AML_INTERNAL; + goto clean_up_and_bail_out; + } + + /* + * Acpi_everything is ready to execute now, We have + * a value we can handle, just perform the update + */ + + switch (destination_type) + { + /* Type of Name's existing value */ + + case INTERNAL_TYPE_BANK_FIELD: + + /* + * Get the global lock if needed + */ + locked = acpi_aml_acquire_global_lock (dest_desc->bank_field.lock_rule); + + /* + * Set Bank value to select proper Bank + * Perform the update (Set Bank Select) + */ + + status = acpi_aml_set_named_field_value (dest_desc->bank_field.bank_select, + &dest_desc->bank_field.value, + sizeof (dest_desc->bank_field.value)); + if (status == AE_OK) { + /* Set bank select successful, set data value */ + + status = acpi_aml_set_named_field_value (dest_desc->bank_field.bank_select, + &val_desc->bank_field.value, + sizeof (val_desc->bank_field.value)); + } + + break; + + + case INTERNAL_TYPE_DEF_FIELD: + + /* + * Get the global lock if needed + */ + locked = acpi_aml_acquire_global_lock (val_desc->field.lock_rule); + + /* + * Perform the update + */ + + switch (val_desc->common.type) + { + case ACPI_TYPE_NUMBER: + buffer = (u8 *) &val_desc->number.value; + length = sizeof (val_desc->number.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = (u8 *) val_desc->buffer.pointer; + length = val_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = (u8 *) val_desc->string.pointer; + length = val_desc->string.length; + break; + } + + status = acpi_aml_set_named_field_value (entry, buffer, length); + break; /* Global Lock released below */ + + + case ACPI_TYPE_STRING: + + /* + * Perform the update + */ + + switch (val_desc->common.type) + { + case ACPI_TYPE_NUMBER: + buffer = (u8 *) &val_desc->number.value; + length = sizeof (val_desc->number.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = (u8 *) val_desc->buffer.pointer; + length = val_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = (u8 *) val_desc->string.pointer; + length = val_desc->string.length; + break; + } + + /* + * Setting a string value replaces the old string + */ + + if (length < dest_desc->string.length) { + /* + * Zero fill, not willing to do pointer arithmetic for + * archetecture independance. Just clear the whole thing + */ + MEMSET(dest_desc->string.pointer, 0, dest_desc->string.length); + MEMCPY(dest_desc->string.pointer, buffer, length); + } + else { + /* + * Free the current buffer, then allocate a buffer + * large enough to hold the value + */ + if ( dest_desc->string.pointer && + !acpi_tb_system_table_pointer (dest_desc->string.pointer)) + { + /* + * Only free if not a pointer into the DSDT + */ + + acpi_cm_free(dest_desc->string.pointer); + } + + dest_desc->string.pointer = acpi_cm_allocate (length + 1); + dest_desc->string.length = length; + + if (!dest_desc->string.pointer) { + status = AE_NO_MEMORY; + goto clean_up_and_bail_out; + } + + MEMCPY(dest_desc->string.pointer, buffer, length); + } + break; + + + case ACPI_TYPE_BUFFER: + + /* + * Perform the update to the buffer + */ + + switch (val_desc->common.type) + { + case ACPI_TYPE_NUMBER: + buffer = (u8 *) &val_desc->number.value; + length = sizeof (val_desc->number.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = (u8 *) val_desc->buffer.pointer; + length = val_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = (u8 *) val_desc->string.pointer; + length = val_desc->string.length; + break; + } + + /* + * Buffer is a static allocation, + * only place what will fit in the buffer. + */ + if (length <= dest_desc->buffer.length) { + /* + * Zero fill first, not willing to do pointer arithmetic for + * archetecture independence. Just clear the whole thing + */ + MEMSET(dest_desc->buffer.pointer, 0, dest_desc->buffer.length); + MEMCPY(dest_desc->buffer.pointer, buffer, length); + } + else { + /* + * truncate, copy only what will fit + */ + MEMCPY(dest_desc->buffer.pointer, buffer, dest_desc->buffer.length); + } + break; + + + case INTERNAL_TYPE_INDEX_FIELD: + + /* + * Get the global lock if needed + */ + locked = acpi_aml_acquire_global_lock (dest_desc->index_field.lock_rule); + + /* + * Set Index value to select proper Data register + * perform the update (Set index) + */ + + status = acpi_aml_set_named_field_value (dest_desc->index_field.index, + &dest_desc->index_field.value, + sizeof (dest_desc->index_field.value)); + + if (AE_OK == status) { + /* set index successful, next set Data value */ + + status = acpi_aml_set_named_field_value (dest_desc->index_field.data, + &val_desc->number.value, + sizeof (val_desc->number.value)); + } + break; + + + case ACPI_TYPE_FIELD_UNIT: + + if ((!dest_desc->field_unit.container || + ACPI_TYPE_BUFFER != dest_desc->field_unit.container->common.type || + dest_desc->field_unit.sequence != + dest_desc->field_unit.container->buffer.sequence)) + { + status = AE_AML_INTERNAL; + goto clean_up_and_bail_out; + } + + /* + * Get the global lock if needed + */ + locked = acpi_aml_acquire_global_lock (dest_desc->field_unit.lock_rule); + + /* + * TBD: [Unhandled] REMOVE this limitation + * Make sure the operation is within the limits of our implementation + * this is not a Spec limitation!! + */ + if (dest_desc->field_unit.length + dest_desc->field_unit.bit_offset > 32) { + status = AE_NOT_IMPLEMENTED; + goto clean_up_and_bail_out; + } + + /* Field location is (base of buffer) + (byte offset) */ + + location = dest_desc->field_unit.container->buffer.pointer + + dest_desc->field_unit.offset; + + /* + * Construct Mask with 1 bits where the field is, + * 0 bits elsewhere + */ + mask = ((u32) 1 << dest_desc->field_unit.length) - ((u32)1 + << dest_desc->field_unit.bit_offset); + + /* Zero out the field in the buffer */ + + MOVE_UNALIGNED32_TO_32 (&new_value, location); + new_value &= ~mask; + + /* + * Shift and mask the new value into position, + * and or it into the buffer. + */ + new_value |= (val_desc->number.value << dest_desc->field_unit.bit_offset) & + mask; + + /* Store back the value */ + + MOVE_UNALIGNED32_TO_32 (location, &new_value); + + break; + + + case ACPI_TYPE_NUMBER: + + dest_desc->number.value = val_desc->number.value; + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * TBD: [Unhandled] Not real sure what to do here + */ + status = AE_NOT_IMPLEMENTED; + break; + + + default: + + /* + * All other types than Alias and the various Fields come here. + * Store Val_desc as the new value of the Name, and set + * the Name's type to that of the value being stored in it. + * Val_desc reference count is incremented by Attach_object. + */ + + status = AE_NOT_IMPLEMENTED; + break; + } + + +clean_up_and_bail_out: + + /* + * Release global lock if we acquired it earlier + */ + acpi_aml_release_global_lock (locked); + + return (status); +} + + diff --git a/drivers/acpi/interpreter/amstorob.c b/drivers/acpi/interpreter/amstorob.c new file mode 100644 index 000000000..6b63108d4 --- /dev/null +++ b/drivers/acpi/interpreter/amstorob.c @@ -0,0 +1,314 @@ + +/****************************************************************************** + * + * Module Name: amstorob - AML Interpreter object store support, store to object + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "tables.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amstorob"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_store_object_to_object + * + * PARAMETERS: *Val_desc - Value to be stored + * *Dest_desc - Object to receive the value + * + * RETURN: Status + * + * DESCRIPTION: Store an object to another object. + * + * The Assignment of an object to another (not named) object + * is handled here. + * The val passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. + * + * This module allows destination types of Number, String, + * and Buffer. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_store_object_to_object ( + ACPI_OBJECT_INTERNAL *val_desc, + ACPI_OBJECT_INTERNAL *dest_desc) +{ + ACPI_STATUS status = AE_OK; + u8 *buffer = NULL; + u32 length = 0; + OBJECT_TYPE_INTERNAL destination_type = dest_desc->common.type; + + + /* + * Assuming the parameters are valid!!! + */ + ACPI_ASSERT((dest_desc) && (val_desc)); + + /* + * First ensure we have a value that can be stored in the target + */ + switch (destination_type) + { + /* Type of Name's existing value */ + + case ACPI_TYPE_NUMBER: + + /* + * These cases all require only number values or values that + * can be converted to numbers. + * + * If value is not a Number, try to resolve it to one. + */ + + if (val_desc->common.type != ACPI_TYPE_NUMBER) { + /* + * Initially not a number, convert + */ + status = acpi_aml_resolve_to_value (&val_desc); + if ((status == AE_OK) && + (val_desc->common.type != ACPI_TYPE_NUMBER)) + { + /* + * Conversion successful but still not a number + */ + status = AE_AML_OPERAND_TYPE; + } + } + + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * Storing into a Field in a region or into a buffer or into + * a string all is essentially the same. + * + * If value is not a valid type, try to resolve it to one. + */ + + if ((val_desc->common.type != ACPI_TYPE_NUMBER) && + (val_desc->common.type != ACPI_TYPE_BUFFER) && + (val_desc->common.type != ACPI_TYPE_STRING)) + { + /* + * Initially not a valid type, convert + */ + status = acpi_aml_resolve_to_value (&val_desc); + if ((status == AE_OK) && + (val_desc->common.type != ACPI_TYPE_NUMBER) && + (val_desc->common.type != ACPI_TYPE_BUFFER) && + (val_desc->common.type != ACPI_TYPE_STRING)) + { + /* + * Conversion successful but still not a valid type + */ + status = AE_AML_OPERAND_TYPE; + } + } + break; + + + default: + + /* + * TBD: [Unhandled] What other combinations must be implemented? + */ + status = AE_NOT_IMPLEMENTED; + break; + } + + /* Exit now if failure above */ + + if (status != AE_OK) { + goto clean_up_and_bail_out; + } + + /* + * Acpi_everything is ready to execute now, We have + * a value we can handle, just perform the update + */ + + switch (destination_type) + { + + case ACPI_TYPE_STRING: + + /* + * Perform the update + */ + + switch (val_desc->common.type) + { + case ACPI_TYPE_NUMBER: + buffer = (u8 *) &val_desc->number.value; + length = sizeof (val_desc->number.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = (u8 *) val_desc->buffer.pointer; + length = val_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = (u8 *) val_desc->string.pointer; + length = val_desc->string.length; + break; + } + + /* + * Setting a string value replaces the old string + */ + + if (length < dest_desc->string.length) { + /* + * Zero fill, not willing to do pointer arithmetic for + * architecture independence. Just clear the whole thing + */ + MEMSET(dest_desc->string.pointer, 0, dest_desc->string.length); + MEMCPY(dest_desc->string.pointer, buffer, length); + } + else { + /* + * Free the current buffer, then allocate a buffer + * large enough to hold the value + */ + if ( dest_desc->string.pointer && + !acpi_tb_system_table_pointer (dest_desc->string.pointer)) + { + /* + * Only free if not a pointer into the DSDT + */ + + acpi_cm_free(dest_desc->string.pointer); + } + + dest_desc->string.pointer = acpi_cm_allocate (length + 1); + dest_desc->string.length = length; + + if (!dest_desc->string.pointer) { + status = AE_NO_MEMORY; + goto clean_up_and_bail_out; + } + + MEMCPY(dest_desc->string.pointer, buffer, length); + } + break; + + + case ACPI_TYPE_BUFFER: + + /* + * Perform the update to the buffer + */ + + switch (val_desc->common.type) + { + case ACPI_TYPE_NUMBER: + buffer = (u8 *) &val_desc->number.value; + length = sizeof (val_desc->number.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = (u8 *) val_desc->buffer.pointer; + length = val_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = (u8 *) val_desc->string.pointer; + length = val_desc->string.length; + break; + } + + /* + * If the buffer is uninitialized, + * memory needs to be allocated for the copy. + */ + if(0 == dest_desc->buffer.length) { + dest_desc->buffer.pointer = acpi_cm_callocate(length); + dest_desc->buffer.length = length; + + if (!dest_desc->buffer.pointer) { + status = AE_NO_MEMORY; + goto clean_up_and_bail_out; + } + } + + /* + * Buffer is a static allocation, + * only place what will fit in the buffer. + */ + if (length <= dest_desc->buffer.length) { + /* + * Zero fill first, not willing to do pointer arithmetic for + * architecture independence. Just clear the whole thing + */ + MEMSET(dest_desc->buffer.pointer, 0, dest_desc->buffer.length); + MEMCPY(dest_desc->buffer.pointer, buffer, length); + } + else { + /* + * truncate, copy only what will fit + */ + MEMCPY(dest_desc->buffer.pointer, buffer, dest_desc->buffer.length); + } + break; + + case ACPI_TYPE_NUMBER: + + dest_desc->number.value = val_desc->number.value; + break; + + default: + + /* + * All other types than Alias and the various Fields come here. + * Store Val_desc as the new value of the Name, and set + * the Name's type to that of the value being stored in it. + * Val_desc reference count is incremented by Attach_object. + */ + + status = AE_NOT_IMPLEMENTED; + break; + } + +clean_up_and_bail_out: + + return (status); +} + diff --git a/drivers/acpi/interpreter/amsystem.c b/drivers/acpi/interpreter/amsystem.c new file mode 100644 index 000000000..9207a2e72 --- /dev/null +++ b/drivers/acpi/interpreter/amsystem.c @@ -0,0 +1,343 @@ + +/****************************************************************************** + * + * Module Name: amsystem - Interface to OS services + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "hardware.h" +#include "events.h" + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amsystem"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_thread_id + * + * PARAMETERS: None + * + * RETURN: Current Thread ID (for this implementation a 1 is returned) + * + * DESCRIPTION: An invocation is identified by its Thread ID. In a single + * threaded OS the Thread ID is undefined so a 1 will be + * returned. + * + ******************************************************************************/ + +u16 +acpi_aml_system_thread_id (void) +{ + return (1); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_wait_semaphore + * + * PARAMETERS: Semaphore - OSD semaphore to wait on + * Timeout - Max time to wait + * + * RETURN: Status + * + * DESCRIPTION: Implements a semaphore wait with a check to see if the + * semaphore is available immediately. If it is not, the + * interpreter is released. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_wait_semaphore ( + ACPI_HANDLE semaphore, + u32 timeout) +{ + ACPI_STATUS status; + + + status = acpi_os_wait_semaphore (semaphore, 1, 0); + if (ACPI_SUCCESS (status)) { + return (status); + } + + if (status == AE_TIME) { + /* We must wait, so unlock the interpreter */ + + acpi_aml_exit_interpreter (); + + status = acpi_os_wait_semaphore (semaphore, 1, timeout); + + /* Reacquire the interpreter */ + + acpi_aml_enter_interpreter (); + + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_do_stall + * + * PARAMETERS: How_long - The amount of time to stall + * + * RETURN: None + * + * DESCRIPTION: Suspend running thread for specified amount of time. + * + ******************************************************************************/ + +void +acpi_aml_system_do_stall ( + u32 how_long) +{ + + if (how_long > 1000) /* 1 millisecond */ { + /* Since this thread will sleep, we must release the interpreter */ + + acpi_aml_exit_interpreter (); + + acpi_os_sleep_usec (how_long); + + /* And now we must get the interpreter again */ + + acpi_aml_enter_interpreter (); + } + + else { + acpi_os_sleep_usec (how_long); + } +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_do_suspend + * + * PARAMETERS: How_long - The amount of time to suspend + * + * RETURN: None + * + * DESCRIPTION: Suspend running thread for specified amount of time. + * + ******************************************************************************/ + +void +acpi_aml_system_do_suspend ( + u32 how_long) +{ + /* Since this thread will sleep, we must release the interpreter */ + + acpi_aml_exit_interpreter (); + + acpi_os_sleep ((u16) (how_long / (u32) 1000), + (u16) (how_long % (u32) 1000)); + + /* And now we must get the interpreter again */ + + acpi_aml_enter_interpreter (); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_acquire_mutex + * + * PARAMETERS: *Time_desc - The 'time to delay' object descriptor + * *Obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This function will cause a lock to be generated + * for the Mutex pointed to by Obj_desc. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_acquire_mutex ( + ACPI_OBJECT_INTERNAL *time_desc, + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (!obj_desc) { + return (AE_BAD_PARAMETER); + } + + /* + * Support for the _GL_ Mutex object -- go get the global lock + */ + + if (obj_desc->mutex.semaphore == acpi_gbl_global_lock_semaphore) { + status = acpi_ev_acquire_global_lock (); + return (status); + } + + status = acpi_aml_system_wait_semaphore (obj_desc->mutex.semaphore, + time_desc->number.value); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_release_mutex + * + * PARAMETERS: *Obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This operation is a request to release a + * previously acquired Mutex. If the Mutex variable is set then + * it will be decremented. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_release_mutex ( + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (!obj_desc) { + return (AE_BAD_PARAMETER); + } + + /* + * Support for the _GL_ Mutex object -- release the global lock + */ + if (obj_desc->mutex.semaphore == acpi_gbl_global_lock_semaphore) { + acpi_ev_release_global_lock (); + return (AE_OK); + } + + status = acpi_os_signal_semaphore (obj_desc->mutex.semaphore, 1); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_signal_event + * + * PARAMETERS: *Obj_desc - The object descriptor for this op + * + * RETURN: AE_OK + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_signal_event ( + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (obj_desc) { + status = acpi_os_signal_semaphore (obj_desc->event.semaphore, 1); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_wait_event + * + * PARAMETERS: *Time_desc - The 'time to delay' object descriptor + * *Obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This operation is a request to wait for an + * event. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_wait_event ( + ACPI_OBJECT_INTERNAL *time_desc, + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + + + if (obj_desc) { + status = acpi_aml_system_wait_semaphore (obj_desc->event.semaphore, + time_desc->number.value); + } + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_system_reset_event + * + * PARAMETERS: *Obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_system_reset_event ( + ACPI_OBJECT_INTERNAL *obj_desc) +{ + ACPI_STATUS status = AE_OK; + void *temp_semaphore; + + + /* + * We are going to simply delete the existing semaphore and + * create a new one! + */ + + status = acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, 0, &temp_semaphore); + if (ACPI_SUCCESS (status)) { + acpi_os_delete_semaphore (obj_desc->mutex.semaphore); + obj_desc->mutex.semaphore = temp_semaphore; + } + + return (status); +} + diff --git a/drivers/acpi/interpreter/amutils.c b/drivers/acpi/interpreter/amutils.c new file mode 100644 index 000000000..1e10e28cb --- /dev/null +++ b/drivers/acpi/interpreter/amutils.c @@ -0,0 +1,522 @@ + +/****************************************************************************** + * + * Module Name: amutils - interpreter/scanner utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" +#include "events.h" + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amutils"); + + +typedef struct internal_search_st +{ + ACPI_OBJECT_INTERNAL *dest_obj; + u32 index; + ACPI_OBJECT_INTERNAL *source_obj; + +} INTERNAL_PKG_SEARCH_INFO; + + +/* Used to traverse nested packages when copying*/ + +INTERNAL_PKG_SEARCH_INFO copy_level[MAX_PACKAGE_DEPTH]; + + +static char hex[] = + {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_enter_interpreter + * + * PARAMETERS: None + * + * DESCRIPTION: Enter the interpreter execution region + * + ******************************************************************************/ + +void +acpi_aml_enter_interpreter (void) +{ + + acpi_cm_acquire_mutex (ACPI_MTX_EXECUTE); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_exit_interpreter + * + * PARAMETERS: None + * + * DESCRIPTION: Exit the interpreter execution region + * + * Cases where the interpreter is unlocked: + * 1) Completion of the execution of a control method + * 2) Method blocked on a Sleep() AML opcode + * 3) Method blocked on an Acquire() AML opcode + * 4) Method blocked on a Wait() AML opcode + * 5) Method blocked to acquire the global lock + * 6) Method blocked to execute a serialized control method that is + * already executing + * 7) About to invoke a user-installed opregion handler + * + ******************************************************************************/ + +void +acpi_aml_exit_interpreter (void) +{ + + acpi_cm_release_mutex (ACPI_MTX_EXECUTE); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_validate_object_type + * + * PARAMETERS: Type Object type to validate + * + * DESCRIPTION: Determine if a type is a valid ACPI object type + * + ******************************************************************************/ + +u8 +acpi_aml_validate_object_type ( + ACPI_OBJECT_TYPE type) +{ + + if ((type > ACPI_TYPE_MAX && type < INTERNAL_TYPE_BEGIN) || + (type > INTERNAL_TYPE_MAX)) + { + return FALSE; + } + + return TRUE; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_append_operand_diag + * + * PARAMETERS: *File_name - Name of source file + * Line_num - Line Number in file + * Op_code - Op_code being executed + * Num_operands - Number of operands Prep_stack tried to check + * + * DESCRIPTION: Print diagnostic information about operands. + * This function is intended to be called after Prep_stack + * has returned S_ERROR. + * + ******************************************************************************/ + +void +acpi_aml_append_operand_diag ( + char *file_name, + s32 line_num, + u16 op_code, + ACPI_OBJECT_INTERNAL **operands, + s32 num_operands) +{ + + /* + * This function outputs debug information only + */ + +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_buf_seq + * + * RETURN: The next buffer descriptor sequence number + * + * DESCRIPTION: Provide a unique sequence number for each Buffer descriptor + * allocated during the interpreter's existence. These numbers + * are used to relate Field_unit descriptors to the Buffers + * within which the fields are defined. + * + * Just increment the global counter and return it. + * + ******************************************************************************/ + +u32 +acpi_aml_buf_seq (void) +{ + + return ++acpi_gbl_buf_seq; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_acquire_global_lock + * + * PARAMETERS: Rule - Lock rule: Always_lock, Never_lock + * + * RETURN: TRUE/FALSE indicating whether the lock was actually acquired + * + * DESCRIPTION: Obtain the global lock and keep track of this fact via two + * methods. A global variable keeps the state of the lock, and + * the state is returned to the caller. + * + ******************************************************************************/ + +u8 +acpi_aml_acquire_global_lock ( + u32 rule) +{ + u8 locked = FALSE; + ACPI_STATUS status; + + + /* Only attempt lock if the Rule says so */ + + if (rule == (u32) GLOCK_ALWAYS_LOCK) { + /* OK to get the lock */ + + status = acpi_ev_acquire_global_lock (); + + if (ACPI_SUCCESS (status)) { + acpi_gbl_global_lock_set = TRUE; + locked = TRUE; + } + } + + return (locked); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_release_global_lock + * + * PARAMETERS: Locked_by_me - Return value from corresponding call to + * Acquire_global_lock. + * + * RETURN: Status + * + * DESCRIPTION: Release the global lock if it is locked. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_release_global_lock ( + u8 locked_by_me) +{ + + + /* Only attempt unlock if the caller locked it */ + + if (locked_by_me) { + /* Double check against the global flag */ + + if (acpi_gbl_global_lock_set) { + /* OK, now release the lock */ + + acpi_ev_release_global_lock (); + acpi_gbl_global_lock_set = FALSE; + } + + } + + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_digits_needed + * + * PARAMETERS: val - Value to be represented + * base - Base of representation + * + * RETURN: the number of digits needed to represent val in base + * + ******************************************************************************/ + +s32 +acpi_aml_digits_needed ( + s32 val, + s32 base) +{ + s32 num_digits = 0; + + + if (base < 1) { + /* impossible base */ + + REPORT_ERROR ("Aml_digits_needed: Impossible base"); + } + + else { + for (num_digits = 1 + (val < 0) ; val /= base ; ++num_digits) { ; } + } + + return (num_digits); +} + + +/******************************************************************************* + * + * FUNCTION: ntohl + * + * PARAMETERS: Value - Value to be converted + * + * RETURN: Convert a 32-bit value to big-endian (swap the bytes) + * + ******************************************************************************/ + +u32 +_ntohl ( + u32 value) +{ + union + { + u32 value; + char bytes[4]; + } out; + + union + { + u32 value; + char bytes[4]; + } in; + + + in.value = value; + + out.bytes[0] = in.bytes[3]; + out.bytes[1] = in.bytes[2]; + out.bytes[2] = in.bytes[1]; + out.bytes[3] = in.bytes[0]; + + return out.value; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_eisa_id_to_string + * + * PARAMETERS: Numeric_id - EISA ID to be converted + * Out_string - Where to put the converted string (8 bytes) + * + * RETURN: Convert a numeric EISA ID to string representation + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_eisa_id_to_string ( + u32 numeric_id, + char *out_string) +{ + u32 id; + + /* swap to big-endian to get contiguous bits */ + + id = _ntohl (numeric_id); + + out_string[0] = (char) ('@' + ((id >> 26) & 0x1f)); + out_string[1] = (char) ('@' + ((id >> 21) & 0x1f)); + out_string[2] = (char) ('@' + ((id >> 16) & 0x1f)); + out_string[3] = hex[(id >> 12) & 0xf]; + out_string[4] = hex[(id >> 8) & 0xf]; + out_string[5] = hex[(id >> 4) & 0xf]; + out_string[6] = hex[id & 0xf]; + out_string[7] = 0; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_build_copy_internal_package_object + * + * PARAMETERS: *Source_obj - Pointer to the source package object + * *Dest_obj - Where the internal object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to copy an internal package object + * into another internal package object. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_build_copy_internal_package_object ( + ACPI_OBJECT_INTERNAL *source_obj, + ACPI_OBJECT_INTERNAL *dest_obj) +{ + u32 current_depth = 0; + ACPI_STATUS status = AE_OK; + u32 length = 0; + u32 this_index; + u32 object_space = 0; + ACPI_OBJECT_INTERNAL *this_dest_obj; + ACPI_OBJECT_INTERNAL *this_source_obj; + INTERNAL_PKG_SEARCH_INFO *level_ptr; + + + /* + * Initialize the working variables + */ + + MEMSET ((void *) copy_level, 0, sizeof(copy_level)); + + copy_level[0].dest_obj = dest_obj; + copy_level[0].source_obj = source_obj; + level_ptr = ©_level[0]; + current_depth = 0; + + dest_obj->common.type = source_obj->common.type; + dest_obj->package.count = source_obj->package.count; + + + /* + * Build an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + + dest_obj->package.elements = acpi_cm_callocate ( + (dest_obj->package.count + 1) * + sizeof (void *)); + if (!dest_obj->package.elements) { + /* Package vector allocation failure */ + + REPORT_ERROR ("Aml_build_copy_internal_package_object: Package vector allocation failure"); + return (AE_NO_MEMORY); + } + + dest_obj->package.next_element = dest_obj->package.elements; + + + while (1) { + this_index = level_ptr->index; + this_dest_obj = (ACPI_OBJECT_INTERNAL *) level_ptr->dest_obj->package.elements[this_index]; + this_source_obj = (ACPI_OBJECT_INTERNAL *) level_ptr->source_obj->package.elements[this_index]; + + if (IS_THIS_OBJECT_TYPE (this_source_obj, ACPI_TYPE_PACKAGE)) { + /* + * If this object is a package then we go one deeper + */ + if (current_depth >= MAX_PACKAGE_DEPTH-1) { + /* + * Too many nested levels of packages for us to handle + */ + return (AE_LIMIT); + } + + /* + * Build the package object + */ + this_dest_obj = acpi_cm_create_internal_object (ACPI_TYPE_PACKAGE); + level_ptr->dest_obj->package.elements[this_index] = this_dest_obj; + + + this_dest_obj->common.type = ACPI_TYPE_PACKAGE; + this_dest_obj->package.count = this_dest_obj->package.count; + + /* + * Save space for the array of objects (Package elements) + * update the buffer length counter + */ + object_space = this_dest_obj->package.count * + sizeof (ACPI_OBJECT_INTERNAL); + length += object_space; + current_depth++; + level_ptr = ©_level[current_depth]; + level_ptr->dest_obj = this_dest_obj; + level_ptr->source_obj = this_source_obj; + level_ptr->index = 0; + + } /* if object is a package */ + + else { + + this_dest_obj = acpi_cm_create_internal_object ( + this_source_obj->common.type); + level_ptr->dest_obj->package.elements[this_index] = this_dest_obj; + + status = acpi_aml_store_object_to_object(this_source_obj, this_dest_obj); + + if (status != AE_OK) { + /* + * Failure get out + */ + return (status); + } + + length +=object_space; + + level_ptr->index++; + while (level_ptr->index >= level_ptr->dest_obj->package.count) { + /* + * We've handled all of the objects at this level, This means + * that we have just completed a package. That package may + * have contained one or more packages itself + */ + if (current_depth == 0) { + /* + * We have handled all of the objects in the top level + * package just add the length of the package objects + * and exit + */ + return (AE_OK); + } + + /* + * Go back up a level and move the index past the just + * completed package object. + */ + current_depth--; + level_ptr = ©_level[current_depth]; + level_ptr->index++; + } + } /* else object is NOT a package */ + } /* while (1) */ + + + /* + * We'll never get here, but the compiler whines about return value + */ + return (AE_OK); +} + + diff --git a/drivers/acpi/interpreter/amxface.c b/drivers/acpi/interpreter/amxface.c new file mode 100644 index 000000000..74a92e813 --- /dev/null +++ b/drivers/acpi/interpreter/amxface.c @@ -0,0 +1,94 @@ + +/****************************************************************************** + * + * Module Name: ixface - External interpreter interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" + + +#define _COMPONENT INTERPRETER + MODULE_NAME ("amxface"); + + +/* + * DEFINE_AML_GLOBALS is tested in amlcode.h + * to determine whether certain global names should be "defined" or only + * "declared" in the current compilation. This enhances maintainability + * by enabling a single header file to embody all knowledge of the names + * in question. + * + * Exactly one module of any executable should #define DEFINE_GLOBALS + * before #including the header files which use this convention. The + * names in question will be defined and initialized in that module, + * and declared as extern in all other modules which #include those + * header files. + */ + +#define DEFINE_AML_GLOBALS +#include "amlcode.h" +#include "parser.h" +#include "namesp.h" + + +/******************************************************************************* + * + * FUNCTION: Acpi_aml_execute_method + * + * PARAMETERS: Pcode - Pointer to the pcode stream + * Pcode_length - Length of pcode that comprises the method + * **Params - List of parameters to pass to method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method + * + ******************************************************************************/ + +ACPI_STATUS +acpi_aml_execute_method ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc) +{ + ACPI_STATUS status; + + + /* + * The point here is to lock the interpreter and call the low + * level execute. + */ + + acpi_aml_enter_interpreter (); + + status = acpi_psx_execute (method_entry, params, return_obj_desc); + + acpi_aml_exit_interpreter (); + + return (status); +} + + diff --git a/drivers/acpi/namespace/nsaccess.c b/drivers/acpi/namespace/nsaccess.c new file mode 100644 index 000000000..7cb9ac409 --- /dev/null +++ b/drivers/acpi/namespace/nsaccess.c @@ -0,0 +1,647 @@ + +/****************************************************************************** + * + * Module Name: nsaccess - Top-level functions for accessing ACPI namespace + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "interp.h" +#include "namesp.h" +#include "dispatch.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsaccess"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_root_create_scope + * + * PARAMETERS: Entry - NTE for which a scope will be created + * + * RETURN: Status + * + * DESCRIPTION: Create a scope table for the given name table entry + * + * MUTEX: Expects namespace to be locked + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_root_create_scope ( + ACPI_NAMED_OBJECT *entry) +{ + + /* Allocate a scope table */ + + if (entry->child_table) { + return (AE_EXIST); + } + + entry->child_table = acpi_ns_allocate_name_table (NS_TABLE_SIZE); + if (!entry->child_table) { + /* root name table allocation failure */ + + REPORT_ERROR ("Root name table allocation failure"); + return (AE_NO_MEMORY); + } + + /* + * Init the scope first entry -- since it is the exemplar of + * the scope (Some fields are duplicated to new entries!) + */ + acpi_ns_initialize_table (entry->child_table, NULL, entry); + return (AE_OK); + +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_root_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Allocate and initialize the root name table + * + * MUTEX: Locks namespace for entire execution + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_root_initialize (void) +{ + ACPI_STATUS status = AE_OK; + PREDEFINED_NAMES *init_val = NULL; + ACPI_NAMED_OBJECT *new_entry; + ACPI_OBJECT_INTERNAL *obj_desc; + + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* + * Root is initially NULL, so a non-NULL value indicates + * that Acpi_ns_root_initialize() has already been called; just return. + */ + + if (acpi_gbl_root_object->child_table) { + status = AE_OK; + goto unlock_and_exit; + } + + + /* Create the root scope */ + + status = acpi_ns_root_create_scope (acpi_gbl_root_object); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Enter the pre-defined names in the name table */ + + for (init_val = acpi_gbl_pre_defined_names; init_val->name; init_val++) { + status = acpi_ns_lookup (NULL, init_val->name, + (OBJECT_TYPE_INTERNAL) init_val->type, + IMODE_LOAD_PASS2, NS_NO_UPSEARCH, + NULL, &new_entry); + + /* + * if name entered successfully + * && its entry in Pre_defined_names[] specifies an + * initial value + */ + + if ((status == AE_OK) && + new_entry && init_val->val) + { + /* + * Entry requests an initial value, allocate a + * descriptor for it. + */ + + obj_desc = + acpi_cm_create_internal_object ( + (OBJECT_TYPE_INTERNAL) init_val->type); + + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* + * Convert value string from table entry to + * internal representation. Only types actually + * used for initial values are implemented here. + */ + + switch (init_val->type) + { + + case ACPI_TYPE_NUMBER: + + obj_desc->number.value = + (u32) STRTOUL (init_val->val, NULL, 10); + break; + + + case ACPI_TYPE_STRING: + + obj_desc->string.length = + (u16) STRLEN (init_val->val); + + /* + * Allocate a buffer for the string. All + * String.Pointers must be allocated buffers! + * (makes deletion simpler) + */ + obj_desc->string.pointer = + acpi_cm_allocate ((ACPI_SIZE) + (obj_desc->string.length + 1)); + + if (!obj_desc->string.pointer) { + REPORT_ERROR ("Initial value string" + "allocation failure"); + + acpi_cm_remove_reference (obj_desc); + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + STRCPY ((char *) obj_desc->string.pointer, + init_val->val); + break; + + + case ACPI_TYPE_MUTEX: + + obj_desc->mutex.sync_level = + (u16) STRTOUL (init_val->val, NULL, 10); + + if (STRCMP (init_val->name, "_GL_") == 0) { + /* + * Create a counting semaphore for the + * global lock + */ + status = + acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, + 1, &obj_desc->mutex.semaphore); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + /* + * We just created the mutex for the + * global lock, save it + */ + + acpi_gbl_global_lock_semaphore = + obj_desc->mutex.semaphore; + } + + else { + /* Create a mutex */ + + status = acpi_os_create_semaphore (1, 1, + &obj_desc->mutex.semaphore); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + /* TBD: [Restructure] These fields may be obsolete */ + + obj_desc->mutex.lock_count = 0; + obj_desc->mutex.thread_id = 0; + break; + + + default: + REPORT_ERROR ("Unsupported initial type value"); + acpi_cm_remove_reference (obj_desc); + obj_desc = NULL; + continue; + } + + /* Store pointer to value descriptor in nte */ + + acpi_ns_attach_object (new_entry, obj_desc, + obj_desc->common.type); + } + } + + +unlock_and_exit: + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_lookup + * + * PARAMETERS: Prefix_scope - Search scope if name is not fully qualified + * Pathname - Search pathname, in internal format + * (as represented in the AML stream) + * Type - Type associated with name + * Interpreter_mode - IMODE_LOAD_PASS2 => add name if not found + * Ret_entry - Where the new entry (NTE) is placed + * + * RETURN: Status + * + * DESCRIPTION: Find or enter the passed name in the name space. + * Log an error if name not found in Exec mode. + * + * MUTEX: Assumes namespace is locked. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_lookup ( + ACPI_GENERIC_STATE *scope_info, + char *pathname, + OBJECT_TYPE_INTERNAL type, + OPERATING_MODE interpreter_mode, + u32 flags, + ACPI_WALK_STATE *walk_state, + ACPI_NAMED_OBJECT **ret_entry) +{ + ACPI_STATUS status; + ACPI_NAME_TABLE *prefix_scope; + ACPI_NAME_TABLE *table_to_search = NULL; + ACPI_NAME_TABLE *scope_to_push = NULL; + ACPI_NAMED_OBJECT *this_entry = NULL; + u32 num_segments; + ACPI_NAME simple_name; + u8 null_name_path = FALSE; + OBJECT_TYPE_INTERNAL type_to_check_for; + OBJECT_TYPE_INTERNAL this_search_type; + + if (!ret_entry) { + return (AE_BAD_PARAMETER); + } + + + acpi_gbl_ns_lookup_count++; + + *ret_entry = ENTRY_NOT_FOUND; + if (!acpi_gbl_root_object->child_table) { + /* + * If the name space has not been initialized: + * - In Pass1 of Load mode, we need to initialize it + * before trying to define a name. + * - In Exec mode, there are no names to be found. + */ + + if (IMODE_LOAD_PASS1 == interpreter_mode) { + if ((status = acpi_ns_root_initialize ()) != AE_OK) { + return (status); + } + } + else { + return (AE_NOT_FOUND); + } + } + + + /* + * Get the prefix scope. + * A null scope means use the root scope + */ + + if ((!scope_info) || + (!scope_info->scope.name_table)) + { + prefix_scope = acpi_gbl_root_object->child_table; + } + else { + prefix_scope = scope_info->scope.name_table; + } + + + /* + * This check is explicitly split provide relax the Type_to_check_for + * conditions for Bank_field_defn. Originally, both Bank_field_defn and + * Def_field_defn caused Type_to_check_for to be set to ACPI_TYPE_REGION, + * but the Bank_field_defn may also check for a Field definition as well + * as an Operation_region. + */ + + if (INTERNAL_TYPE_DEF_FIELD_DEFN == type) { + /* Def_field_defn defines fields in a Region */ + + type_to_check_for = ACPI_TYPE_REGION; + } + + else if (INTERNAL_TYPE_BANK_FIELD_DEFN == type) { + /* Bank_field_defn defines data fields in a Field Object */ + + type_to_check_for = ACPI_TYPE_ANY; + } + + else { + type_to_check_for = type; + } + + + /* Examine the name pointer */ + + if (!pathname) { + /* 8-12-98 ASL Grammar Update supports null Name_path */ + + null_name_path = TRUE; + num_segments = 0; + this_entry = acpi_gbl_root_object; + + } + + else { + /* + * Valid name pointer (Internal name format) + * + * Check for prefixes. As represented in the AML stream, a + * Pathname consists of an optional scope prefix followed by + * a segment part. + * + * If present, the scope prefix is either a Root_prefix (in + * which case the name is fully qualified), or zero or more + * Parent_prefixes (in which case the name's scope is relative + * to the current scope). + * + * The segment part consists of either: + * - A single 4-byte name segment, or + * - A Dual_name_prefix followed by two 4-byte name segments, or + * - A Multi_name_prefix_op, followed by a byte indicating the + * number of segments and the segments themselves. + */ + + if (*pathname == AML_ROOT_PREFIX) { + /* Pathname is fully qualified, look in root name table */ + + table_to_search = acpi_gbl_root_object->child_table; + /* point to segment part */ + pathname++; + + /* Direct reference to root, "\" */ + + if (!(*pathname)) { + this_entry = acpi_gbl_root_object; + goto check_for_new_scope_and_exit; + } + } + + else { + /* Pathname is relative to current scope, start there */ + + table_to_search = prefix_scope; + + /* + * Handle up-prefix (carat). More than one prefix + * is supported + */ + + while (*pathname == AML_PARENT_PREFIX) { + + /* Point to segment part or next Parent_prefix */ + + pathname++; + + /* Backup to the parent's scope */ + + table_to_search = table_to_search->parent_table; + if (!table_to_search) { + /* Current scope has no parent scope */ + + REPORT_ERROR ("Ns_lookup: Too many parent" + "prefixes or scope has no parent"); + + + return (AE_NOT_FOUND); + } + } + } + + + /* + * Examine the name prefix opcode, if any, + * to determine the number of segments + */ + + if (*pathname == AML_DUAL_NAME_PREFIX) { + num_segments = 2; + /* point to first segment */ + pathname++; + + } + + else if (*pathname == AML_MULTI_NAME_PREFIX_OP) { + num_segments = (s32)* (u8 *) ++pathname; + /* point to first segment */ + pathname++; + + } + + else { + /* + * No Dual or Multi prefix, hence there is only one + * segment and Pathname is already pointing to it. + */ + num_segments = 1; + + } + + } + + + /* + * Search namespace for each segment of the name. + * Loop through and verify/add each name segment. + */ + + + while (num_segments-- && table_to_search) { + /* + * Search for the current segment in the table where + * it should be. + * Type is significant only at the last (topmost) level. + */ + this_search_type = ACPI_TYPE_ANY; + if (!num_segments) { + this_search_type = type; + } + + MOVE_UNALIGNED32_TO_32 (&simple_name, pathname); + status = acpi_ns_search_and_enter (simple_name, walk_state, + table_to_search, interpreter_mode, + this_search_type, flags, + &this_entry); + + if (status != AE_OK) { + if (status == AE_NOT_FOUND) { + /* Name not in ACPI namespace */ + + if (IMODE_LOAD_PASS1 == interpreter_mode || + IMODE_LOAD_PASS2 == interpreter_mode) + { + REPORT_ERROR ("Name table overflow"); + } + + } + + return (status); + } + + + /* + * If 1) last segment (Num_segments == 0) + * 2) and looking for a specific type + * (Not checking for TYPE_ANY) + * 3) which is not a local type (TYPE_DEF_ANY) + * 4) which is not a local type (TYPE_SCOPE) + * 5) which is not a local type (TYPE_INDEX_FIELD_DEFN) + * 6) and type of entry is known (not TYPE_ANY) + * 7) and entry does not match request + * + * Then we have a type mismatch. Just warn and ignore it. + */ + if ((num_segments == 0) && + (type_to_check_for != ACPI_TYPE_ANY) && + (type_to_check_for != INTERNAL_TYPE_DEF_ANY) && + (type_to_check_for != INTERNAL_TYPE_SCOPE) && + (type_to_check_for != INTERNAL_TYPE_INDEX_FIELD_DEFN) && + (this_entry->type != ACPI_TYPE_ANY) && + (this_entry->type != type_to_check_for)) + { + /* Complain about type mismatch */ + + REPORT_WARNING ("Type mismatch"); + } + + /* + * If last segment and not looking for a specific type, but type of + * found entry is known, use that type to see if it opens a scope. + */ + + if ((0 == num_segments) && (ACPI_TYPE_ANY == type)) { + type = this_entry->type; + } + + if ((num_segments || acpi_ns_opens_scope (type)) && + (this_entry->child_table == NULL)) + { + /* + * More segments or the type implies enclosed scope, + * and the next scope has not been allocated. + */ + + if ((IMODE_LOAD_PASS1 == interpreter_mode) || + (IMODE_LOAD_PASS2 == interpreter_mode)) + { + /* + * First or second pass load mode + * ==> locate the next scope + */ + + this_entry->child_table = + acpi_ns_allocate_name_table (NS_TABLE_SIZE); + + if (!this_entry->child_table) { + return (AE_NO_MEMORY); + } + } + + /* Now complain if there is no next scope */ + + if (this_entry->child_table == NULL) { + if (IMODE_LOAD_PASS1 == interpreter_mode || + IMODE_LOAD_PASS2 == interpreter_mode) + { + REPORT_ERROR ("Name Table allocation failure"); + return (AE_NOT_FOUND); + } + + return (AE_NOT_FOUND); + } + + + /* Scope table initialization */ + + if (IMODE_LOAD_PASS1 == interpreter_mode || + IMODE_LOAD_PASS2 == interpreter_mode) + { + /* Initialize the new table */ + + acpi_ns_initialize_table (this_entry->child_table, + table_to_search, + this_entry); + } + } + + table_to_search = this_entry->child_table; + /* point to next name segment */ + pathname += ACPI_NAME_SIZE; + } + + + /* + * Always check if we need to open a new scope + */ + +check_for_new_scope_and_exit: + + if (!(flags & NS_DONT_OPEN_SCOPE) && (walk_state)) { + /* + * If entry is a type which opens a scope, + * push the new scope on the scope stack. + */ + + if (acpi_ns_opens_scope (type_to_check_for)) { + /* 8-12-98 ASL Grammar Update supports null Name_path */ + + if (null_name_path) { + /* TBD: [Investigate] - is this the correct thing to do? */ + + scope_to_push = NULL; + } + else { + scope_to_push = this_entry->child_table; + } + + status = acpi_ds_scope_stack_push (scope_to_push, type, + walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + } + } + + *ret_entry = this_entry; + return (AE_OK); +} + diff --git a/drivers/acpi/namespace/nsalloc.c b/drivers/acpi/namespace/nsalloc.c new file mode 100644 index 000000000..414f3791a --- /dev/null +++ b/drivers/acpi/namespace/nsalloc.c @@ -0,0 +1,411 @@ + +/****************************************************************************** + * + * Module Name: nsalloc - Namespace allocation and deletion utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "namesp.h" +#include "interp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsalloc"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_allocate_name_table + * + * PARAMETERS: Nte_count - Count of NTEs to allocate + * + * RETURN: The address of the first nte in the array, or NULL + * + * DESCRIPTION: Allocate an array of nte, including prepended link space + * Array is set to all zeros via Acpi_os_callcate(). + * + ***************************************************************************/ + +ACPI_NAME_TABLE * +acpi_ns_allocate_name_table ( + u32 num_entries) +{ + ACPI_NAME_TABLE *name_table = NULL; + ACPI_SIZE alloc_size; + + + alloc_size = sizeof (ACPI_NAME_TABLE) + ((num_entries - 1) * + sizeof (ACPI_NAMED_OBJECT)); + + name_table = acpi_cm_callocate (alloc_size); + + + return (name_table); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_delete_namespace_subtree + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete a subtree of the namespace. This includes all objects stored + * within the subtree. Scope tables are deleted also + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_delete_namespace_subtree ( + ACPI_NAMED_OBJECT *parent_entry) +{ + ACPI_NAMED_OBJECT *child_entry; + u32 level; + ACPI_OBJECT_INTERNAL *obj_desc; + + + child_entry = 0; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + + while (level > 0) { + /* + * Get the next typed object in this scope. + * Null returned if not found + */ + + child_entry = acpi_ns_get_next_object (ACPI_TYPE_ANY, + parent_entry, + child_entry); + + if (child_entry) { + /* + * Found an object - delete the object within + * the Value field + */ + + obj_desc = acpi_ns_get_attached_object (child_entry); + if (obj_desc) { + acpi_ns_detach_object (child_entry); + acpi_cm_remove_reference (obj_desc); + } + + + /* + * Clear the NTE in case this scope is reused + * (e.g., a control method scope) + */ + + child_entry->type = ACPI_TYPE_ANY; + child_entry->name = 0; + + /* Check if this object has any children */ + + if (acpi_ns_get_next_object (ACPI_TYPE_ANY, child_entry, 0)) { + /* + * There is at least one child of this object, + * visit the object + */ + + level++; + parent_entry = child_entry; + child_entry = 0; + } + + else { + /* + * There may be a name table even if there are + * no children + */ + + acpi_ns_delete_name_table (child_entry->child_table); + child_entry->child_table = NULL; + + } + } + + else { + /* + * No more children in this object. + * We will move up to the grandparent. + */ + level--; + + /* + * Delete the scope (Name Table) associated with + * the parent object + */ + /* Don't delete the top level scope, this allows + * the dynamic deletion of objects created underneath + * control methods! + */ + + if (level != 0) { + acpi_ns_delete_name_table (parent_entry->child_table); + parent_entry->child_table = NULL; + } + + /* New "last child" is this parent object */ + + child_entry = parent_entry; + + /* Now we can move up the tree to the grandparent */ + + parent_entry = acpi_ns_get_parent_entry (parent_entry); + } + } + + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_remove_reference + * + * PARAMETERS: Entry - NTE whose reference count is to be decremented + * + * RETURN: None. + * + * DESCRIPTION: Remove an NTE reference. Decrements the reference count of + * all parent NTEs up to the root. Any NTE along the way that + * reaches zero references is freed. + * + ***************************************************************************/ + +void +acpi_ns_remove_reference ( + ACPI_NAMED_OBJECT *entry) +{ + ACPI_NAMED_OBJECT *this_entry; + + + /* There may be a name table even if there are no children */ + + acpi_ns_delete_name_table (entry->child_table); + entry->child_table = NULL; + + + /* + * Decrement the reference count(s) of all parents up to the root, + * And delete anything with zero remaining references. + */ + this_entry = entry; + while (this_entry) { + /* Decrement the reference */ + + this_entry->reference_count--; + + /* Delete entry if no more references */ + + if (!this_entry->reference_count) { + /* Delete the scope if present */ + + if (this_entry->child_table) { + acpi_ns_delete_name_table (this_entry->child_table); + this_entry->child_table = NULL; + } + + /* + * Mark the entry free + * (This doesn't deallocate anything) + */ + + acpi_ns_free_table_entry (this_entry); + + } + + /* Move up to parent */ + + this_entry = acpi_ns_get_parent_entry (this_entry); + } +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_delete_namespace_by_owner + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete entries within the namespace that are owned by a + * specific ID. Used to delete entire ACPI tables. All + * reference counts are updated. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_delete_namespace_by_owner ( + u16 owner_id) +{ + ACPI_NAMED_OBJECT *child_entry; + u32 level; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_NAMED_OBJECT *parent_entry; + + + parent_entry = acpi_gbl_root_object; + child_entry = 0; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + + while (level > 0) { + /* + * Get the next typed object in this scope. + * Null returned if not found + */ + + child_entry = acpi_ns_get_next_object (ACPI_TYPE_ANY, + parent_entry, + child_entry); + + if (child_entry) { + if (child_entry->owner_id == owner_id) { + /* + * Found an object - delete the object within + * the Value field + */ + + obj_desc = acpi_ns_get_attached_object (child_entry); + if (obj_desc) { + acpi_ns_detach_object (child_entry); + acpi_cm_remove_reference (obj_desc); + } + } + + /* Check if this object has any children */ + + if (acpi_ns_get_next_object (ACPI_TYPE_ANY, child_entry, 0)) { + /* + * There is at least one child of this object, + * visit the object + */ + + level++; + parent_entry = child_entry; + child_entry = 0; + } + + else if (child_entry->owner_id == owner_id) { + acpi_ns_remove_reference (child_entry); + } + } + + else { + /* + * No more children in this object. + * We will move up to the grandparent. + */ + level--; + + /* + * Delete the scope (Name Table) associated with + * the parent object + */ + /* Don't delete the top level scope, this allows + * the dynamic deletion of objects created underneath + * control methods! + */ + + + if (level != 0) { + if (parent_entry->owner_id == owner_id) { + acpi_ns_remove_reference (parent_entry); + } + } + + + /* New "last child" is this parent object */ + + child_entry = parent_entry; + + /* Now we can move up the tree to the grandparent */ + + parent_entry = acpi_ns_get_parent_entry (parent_entry); + } + } + + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_delete_name_table + * + * PARAMETERS: Scope - A handle to the scope to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete a namespace Name Table with zero or + * more appendages. The table and all appendages are deleted. + * + ***************************************************************************/ + +void +acpi_ns_delete_name_table ( + ACPI_NAME_TABLE *name_table) +{ + ACPI_NAME_TABLE *this_table; + ACPI_NAME_TABLE *next_table; + + + if (!name_table) { + return; + } + + this_table = name_table; + + + /* + * Deallocate the name table and all appendages + */ + do + { + next_table = this_table->next_table; + + /* Now we can free the table */ + + acpi_cm_free (this_table); + this_table = next_table; + + } while (this_table); + + return; +} + + diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c new file mode 100644 index 000000000..ea3e15621 --- /dev/null +++ b/drivers/acpi/namespace/nseval.c @@ -0,0 +1,507 @@ + +/****************************************************************************** + * + * Module Name: nseval - Object evaluation interfaces -- includes control + * method lookup and execution. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nseval"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_evaluate_relative + * + * PARAMETERS: Rel_obj_entry - NTE of the relative containing object + * *Pathname - Name of method to execute, If NULL, the + * handle is the object to execute + * **Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * *Return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * + * RETURN: Status + * + * DESCRIPTION: Find and execute the requested method using the handle as a + * scope + * + * MUTEX: Locks Namespace + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_evaluate_relative ( + ACPI_NAMED_OBJECT *handle, + char *pathname, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object) +{ + ACPI_NAMED_OBJECT *rel_obj_entry; + ACPI_STATUS status; + ACPI_NAMED_OBJECT *obj_entry = NULL; + char *internal_path = NULL; + ACPI_GENERIC_STATE scope_info; + + + /* + * Must have a valid object handle + */ + if (!handle) { + return (AE_BAD_PARAMETER); + } + + /* Build an internal name string for the method */ + + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Get the prefix handle and NTE */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + rel_obj_entry = acpi_ns_convert_handle_to_entry (handle); + if (!rel_obj_entry) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + status = AE_BAD_PARAMETER; + goto cleanup; + } + + /* Lookup the name in the namespace */ + + scope_info.scope.name_table = rel_obj_entry->child_table; + status = acpi_ns_lookup (&scope_info, internal_path, ACPI_TYPE_ANY, + IMODE_EXECUTE, + NS_NO_UPSEARCH, NULL, + &obj_entry); + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + if (status != AE_OK) { + goto cleanup; + } + + /* + * Now that we have a handle to the object, we can attempt + * to evaluate it. + */ + + status = acpi_ns_evaluate_by_handle (obj_entry, params, return_object); + +cleanup: + + /* Cleanup */ + + acpi_cm_free (internal_path); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_evaluate_by_name + * + * PARAMETERS: Pathname - Fully qualified pathname to the object + * *Return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * **Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Find and execute the requested method passing the given + * parameters + * + * MUTEX: Locks Namespace + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_evaluate_by_name ( + char *pathname, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object) +{ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *obj_entry = NULL; + char *internal_path = NULL; + + + /* Build an internal name string for the method */ + + if (pathname[0] != '\\' || pathname[1] != '/') { + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Lookup the name in the namespace */ + + status = acpi_ns_lookup (NULL, internal_path, ACPI_TYPE_ANY, + IMODE_EXECUTE, + NS_NO_UPSEARCH, NULL, + &obj_entry); + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + if (status != AE_OK) { + goto cleanup; + } + + /* + * Now that we have a handle to the object, we can attempt + * to evaluate it. + */ + + status = acpi_ns_evaluate_by_handle (obj_entry, params, return_object); + + +cleanup: + + /* Cleanup */ + + if (internal_path) { + acpi_cm_free (internal_path); + } + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_evaluate_by_handle + * + * PARAMETERS: Obj_entry - NTE of method to execute + * *Return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * **Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Execute the requested method passing the given parameters + * + * MUTEX: Locks Namespace + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_evaluate_by_handle ( + ACPI_NAMED_OBJECT *handle, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_object) +{ + ACPI_NAMED_OBJECT *obj_entry; + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *local_return_object; + + + /* Check if namespace has been initialized */ + + if (!acpi_gbl_root_object->child_table) { + return (AE_NO_NAMESPACE); + } + + /* Parameter Validation */ + + if (!handle) { + return (AE_BAD_PARAMETER); + } + + if (return_object) { + /* Initialize the return value to an invalid object */ + + *return_object = NULL; + } + + /* Get the prefix handle and NTE */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + obj_entry = acpi_ns_convert_handle_to_entry (handle); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + + /* + * Two major cases here: + * 1) The object is an actual control method -- execute it. + * 2) The object is not a method -- just return it's current + * value + * + * In both cases, the namespace is unlocked by the + * Acpi_ns* procedure + */ + + if (acpi_ns_get_type (obj_entry) == ACPI_TYPE_METHOD) { + /* + * Case 1) We have an actual control method to execute + */ + + status = acpi_ns_execute_control_method (obj_entry, + params, + &local_return_object); + } + + else { + /* + * Case 2) Object is NOT a method, just return its + * current value + */ + + status = acpi_ns_get_object_value (obj_entry, + &local_return_object); + } + + + /* + * Check if there is a return value on the stack that must + * be dealt with + */ + + if (status == AE_CTRL_RETURN_VALUE) { + /* + * If the Method returned a value and the caller + * provided a place to store a returned value, Copy + * the returned value to the object descriptor provided + * by the caller. + */ + + if (return_object) { + /* + * Valid return object, copy the pointer to + * the returned object + */ + + *return_object = local_return_object; + } + + + /* Map AE_RETURN_VALUE to AE_OK, we are done with it */ + + if (status == AE_CTRL_RETURN_VALUE) { + status = AE_OK; + } + } + + /* + * Namespace was unlocked by the handling Acpi_ns* function, + * so we just return + */ + + return (status); + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_execute_control_method + * + * PARAMETERS: Method_entry - The Nte of the object/method + * **Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Execute the requested method passing the given parameters + * + * MUTEX: Assumes namespace is locked + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_execute_control_method ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + + + /* Verify that there is a method associated with this object */ + + obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) method_entry); + if (!obj_desc) { + return (AE_ERROR); + } + + /* + * Valid method, Set the current scope to that of the Method, + * and execute it. + */ + + + /* + * Unlock the namespace before execution. This allows namespace access + * via the external Acpi* interfaces while a method is being executed. + * However, any namespace deletion must acquire both the namespace and + * interpter locks to ensure that no thread is using the portion of the + * namespace that is being deleted. + */ + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + /* + * Excecute the method via the interpreter + */ + status = acpi_aml_execute_method (method_entry, params, return_obj_desc); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_object_value + * + * PARAMETERS: Object_entry - The Nte of the object + * + * RETURN: Status + * + * DESCRIPTION: Return the current value of the object + * + * MUTEX: Assumes namespace is locked + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_get_object_value ( + ACPI_NAMED_OBJECT *object_entry, + ACPI_OBJECT_INTERNAL **return_obj_desc) +{ + ACPI_STATUS status = AE_OK; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *val_desc; + + + /* + * We take the value from certain objects directly + */ + + if ((object_entry->type == ACPI_TYPE_PROCESSOR) || + (object_entry->type == ACPI_TYPE_POWER)) + { + + /* + * Create a Reference object to contain the object + */ + obj_desc = acpi_cm_create_internal_object (object_entry->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* + * Get the attached object + */ + + val_desc = acpi_ns_get_attached_object (object_entry); + if (!val_desc) { + status = AE_NULL_OBJECT; + goto unlock_and_exit; + } + + /* + * Just copy from the original to the return object + */ + + MEMCPY (&obj_desc->common.first_non_common_byte, + &val_desc->common.first_non_common_byte, + (sizeof(ACPI_OBJECT_COMMON) - + sizeof(obj_desc->common.first_non_common_byte))); + } + + + /* + * Other objects require a reference object wrapper which we + * then attempt to resolve. + */ + else { + /* Create an Reference object to contain the object */ + + obj_desc = acpi_cm_create_internal_object (INTERNAL_TYPE_REFERENCE); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Construct a descriptor pointing to the name */ + + obj_desc->reference.op_code = (u8) AML_NAME_OP; + obj_desc->reference.object = (void *) object_entry; + + /* + * Use Acpi_aml_resolve_to_value() to get the associated value. + * The call to Acpi_aml_resolve_to_value causes + * Obj_desc (allocated above) to always be deleted. + */ + + status = acpi_aml_resolve_to_value (&obj_desc); + } + + /* + * If Acpi_aml_resolve_to_value() succeeded, the return value was + * placed in Obj_desc. + */ + + if (status == AE_OK) { + status = AE_CTRL_RETURN_VALUE; + + *return_obj_desc = obj_desc; + } + + +unlock_and_exit: + + /* Unlock the namespace */ + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} diff --git a/drivers/acpi/namespace/nsload.c b/drivers/acpi/namespace/nsload.c new file mode 100644 index 000000000..4b6b3f309 --- /dev/null +++ b/drivers/acpi/namespace/nsload.c @@ -0,0 +1,488 @@ + +/****************************************************************************** + * + * Module Name: nsload - namespace loading/expanding/contracting procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "debugger.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsload"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ns_parse_table + * + * PARAMETERS: Table_desc - An ACPI table descriptor for table to parse + * Scope - Where to enter the table into the namespace + * + * RETURN: Status + * + * DESCRIPTION: Parse AML within an ACPI table and return a tree of ops + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ns_parse_table ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAME_TABLE *scope) +{ + ACPI_STATUS status; + + + /* Create the root object */ + + acpi_gbl_parsed_namespace_root = acpi_ps_alloc_op (AML_SCOPE_OP); + if (!acpi_gbl_parsed_namespace_root) { + return (AE_NO_MEMORY); + } + + /* Initialize the root object */ + + ((ACPI_NAMED_OP *) acpi_gbl_parsed_namespace_root)->name = ACPI_ROOT_NAME; + + /* Pass 1: Parse everything except control method bodies */ + + status = acpi_ps_parse_aml (acpi_gbl_parsed_namespace_root, + table_desc->aml_pointer, + table_desc->aml_length, 0); + + if (ACPI_FAILURE (status)) { + return (status); + } + + +#ifndef PARSER_ONLY + status = acpi_ps_walk_parsed_aml (acpi_ps_get_child (acpi_gbl_parsed_namespace_root), + acpi_gbl_parsed_namespace_root, NULL, + scope, NULL, NULL, + table_desc->table_id, + acpi_ds_load2_begin_op, + acpi_ds_load2_end_op); + + + /* + * Now that the internal namespace has been constructed, we can delete the + * parsed namespace, since it is no longer needed + */ + + acpi_ps_delete_parse_tree (acpi_gbl_parsed_namespace_root); + acpi_gbl_parsed_namespace_root = NULL; +#endif + + + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: Acpi_ns_load_table + * + * PARAMETERS: *Pcode_addr - Address of pcode block + * Pcode_length - Length of pcode block + * + * RETURN: Status + * + * DESCRIPTION: Mainline of the AML load/dump subsystem. Sets up the + * input engine, calls handler for outermost object type. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_load_table ( + ACPI_TABLE_DESC *table_desc, + ACPI_NAMED_OBJECT *entry) +{ + ACPI_STATUS status; + + + if (!table_desc->aml_pointer) { + return (AE_BAD_PARAMETER); + } + + + if (!table_desc->aml_length) { + return (AE_BAD_PARAMETER); + } + + + /* + * Parse the table and load the namespace with all named + * objects found within. Control methods are NOT parsed + * at this time. In fact, the control methods cannot be + * parsed until the entire namespace is loaded, because + * if a control method makes a forward reference (call) + * to another control method, we can't continue parsing + * because we don't know how many arguments to parse next! + */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + status = acpi_ns_parse_table (table_desc, entry->child_table); + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Now we can parse the control methods. We always parse + * them here for a sanity check, and if configured for + * just-in-time parsing, we delete the control method + * parse trees. + */ + + status = acpi_ds_initialize_objects (table_desc, entry); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ns_load_table_by_type + * + * PARAMETERS: Table_type - Id of the table type to load + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table or tables into the namespace. All tables + * of the given type are loaded. The mechanism allows this + * routine to be called repeatedly. + * + *****************************************************************************/ + +ACPI_STATUS +acpi_ns_load_table_by_type ( + ACPI_TABLE_TYPE table_type) +{ + u32 i; + ACPI_STATUS status = AE_OK; + ACPI_TABLE_HEADER *table_ptr; + ACPI_TABLE_DESC *table_desc; + + + acpi_cm_acquire_mutex (ACPI_MTX_TABLES); + + + /* + * Table types supported are: + * DSDT (one), SSDT/PSDT (multiple) + */ + + switch (table_type) + { + + case ACPI_TABLE_DSDT: + + table_desc = &acpi_gbl_acpi_tables[ACPI_TABLE_DSDT]; + + /* If table already loaded into namespace, just return */ + + if (table_desc->loaded_into_namespace) { + goto unlock_and_exit; + } + + table_desc->table_id = TABLE_ID_DSDT; + + /* Initialize the root of the namespace tree */ + + status = acpi_ns_root_initialize (); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Now load the single DSDT */ + + status = acpi_ns_load_table (table_desc, acpi_gbl_root_object); + if (ACPI_SUCCESS (status)) { + table_desc->loaded_into_namespace = TRUE; + } + + break; + + + case ACPI_TABLE_SSDT: + + /* + * Traverse list of SSDT tables + */ + + table_desc = &acpi_gbl_acpi_tables[ACPI_TABLE_SSDT]; + for (i = 0; i < acpi_gbl_acpi_tables[ACPI_TABLE_SSDT].count; i++) { + table_ptr = table_desc->pointer; + + /* + * Only attempt to load table if it is not + * already loaded! + */ + + if (!table_desc->loaded_into_namespace) { + status = acpi_ns_load_table (table_desc, + acpi_gbl_root_object); + if (ACPI_FAILURE (status)) { + break; + } + + table_desc->loaded_into_namespace = TRUE; + } + + table_desc = table_desc->next; + } + + break; + + + case ACPI_TABLE_PSDT: + + /* + * Traverse list of PSDT tables + */ + + table_desc = &acpi_gbl_acpi_tables[ACPI_TABLE_PSDT]; + + for (i = 0; i < acpi_gbl_acpi_tables[ACPI_TABLE_PSDT].count; i++) { + table_ptr = table_desc->pointer; + + /* Only attempt to load table if it is not already loaded! */ + + if (!table_desc->loaded_into_namespace) { + status = acpi_ns_load_table (table_desc, + acpi_gbl_root_object); + if (ACPI_FAILURE (status)) { + break; + } + + table_desc->loaded_into_namespace = TRUE; + } + + table_desc = table_desc->next; + } + + break; + + + default: + status = AE_SUPPORT; + } + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_TABLES); + + return (status); + +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ns_free_table_entry + * + * PARAMETERS: Entry - The entry to be deleted + * + * RETURNS None + * + * DESCRIPTION: Free an entry in a namespace table. Delete any objects contained + * in the entry, unlink the entry, then mark it unused. + * + ******************************************************************************/ + +void +acpi_ns_free_table_entry ( + ACPI_NAMED_OBJECT *entry) +{ + + if (!entry) { + return; + } + + /* + * Need to delete + * 1) The scope, if any + * 2) An attached object, if any + */ + + if (entry->child_table) { + acpi_cm_free (entry->child_table); + entry->child_table = NULL; + } + + if (entry->object) { + acpi_ns_detach_object (entry->object); + entry->object = NULL; + } + + /* Mark the entry unallocated */ + + entry->name = 0; + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ns_delete_subtree + * + * PARAMETERS: Start_handle - Handle in namespace where search begins + * + * RETURNS Status + * + * DESCRIPTION: Walks the namespace starting at the given handle and deletes + * all objects, entries, and scopes in the entire subtree. + * + * TBD: [Investigate] What if any part of this subtree is in use? + * (i.e. on one of the object stacks?) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ns_delete_subtree ( + ACPI_HANDLE start_handle) +{ + ACPI_STATUS status; + ACPI_HANDLE child_handle; + ACPI_HANDLE parent_handle; + ACPI_HANDLE next_child_handle; + ACPI_HANDLE dummy; + u32 level; + + + parent_handle = start_handle; + child_handle = 0; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + + while (level > 0) { + /* Attempt to get the next object in this scope */ + + status = acpi_get_next_object (ACPI_TYPE_ANY, parent_handle, + child_handle, + &next_child_handle); + + /* + * Regardless of the success or failure of the + * previous operation, we are done with the previous + * object (if there was one), and any children it + * may have had. So we can now safely delete it (and + * its scope, if any) + */ + + acpi_ns_free_table_entry (child_handle); + child_handle = next_child_handle; + + + /* Did we get a new object? */ + + if (ACPI_SUCCESS (status)) { + /* Check if this object has any children */ + + if (ACPI_SUCCESS (acpi_get_next_object (ACPI_TYPE_ANY, + child_handle, 0, + &dummy))) + { + /* + * There is at least one child of this object, + * visit the object + */ + + level++; + parent_handle = child_handle; + child_handle = 0; + } + } + + else { + /* + * No more children in this object, go back up to + * the object's parent + */ + level--; + child_handle = parent_handle; + acpi_get_parent (parent_handle, &parent_handle); + } + } + + /* Now delete the starting object, and we are done */ + + acpi_ns_free_table_entry ((ACPI_NAMED_OBJECT*) child_handle); + + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_unload_name_space + * + * PARAMETERS: Handle - Root of namespace subtree to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Shrinks the namespace, typically in response to an undocking + * event. Deletes an entire subtree starting from (and + * including) the given handle. + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_unload_namespace ( + ACPI_HANDLE handle) +{ + ACPI_STATUS status; + + + /* Parameter validation */ + + if (!acpi_gbl_root_object->child_table) { + return (AE_NO_NAMESPACE); + } + + if (!handle) { + return (AE_BAD_PARAMETER); + } + + + /* This function does the real work */ + + status = acpi_ns_delete_subtree (handle); + + return (status); +} + + diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c new file mode 100644 index 000000000..1c4ab7c0b --- /dev/null +++ b/drivers/acpi/namespace/nsnames.c @@ -0,0 +1,503 @@ + +/****************************************************************************** + * + * Module Name: nsnames - Name manipulation and search + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsnames"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_name_of_scope + * + * PARAMETERS: Scope - Scope whose name is needed + * + * RETURN: Pointer to storage containing the fully qualified name of + * the scope, in Label format (all segments strung together + * with no separators) + * + * DESCRIPTION: Used via Acpi_ns_name_of_current_scope() and Acpi_ns_last_fQN() + * for label generation in the interpreter, and for debug + * printing in Acpi_ns_search_table(). + * + ***************************************************************************/ + +char * +acpi_ns_name_of_scope ( + ACPI_NAME_TABLE *scope) +{ + char *name_buffer; + ACPI_SIZE size; + ACPI_NAME name; + ACPI_NAMED_OBJECT *entry_to_search; + ACPI_NAMED_OBJECT *parent_entry; + + + if (!acpi_gbl_root_object->child_table || !scope) { + /* + * If the name space has not been initialized, + * this function should not have been called. + */ + return (NULL); + } + + entry_to_search = scope->entries; + + + /* Calculate required buffer size based on depth below root NT */ + + size = 1; + parent_entry = entry_to_search; + while (parent_entry) { + parent_entry = acpi_ns_get_parent_entry (parent_entry); + if (parent_entry) { + size += ACPI_NAME_SIZE; + } + } + + + /* Allocate the buffer */ + + name_buffer = acpi_cm_callocate (size + 1); + if (!name_buffer) { + REPORT_ERROR ("Ns_name_of_scope: allocation failure"); + return (NULL); + } + + + /* Store terminator byte, then build name backwards */ + + name_buffer[size] = '\0'; + while ((size > ACPI_NAME_SIZE) && + acpi_ns_get_parent_entry (entry_to_search)) + { + size -= ACPI_NAME_SIZE; + name = acpi_ns_find_parent_name (entry_to_search); + + /* Put the name into the buffer */ + + MOVE_UNALIGNED32_TO_32 ((name_buffer + size), &name); + entry_to_search = acpi_ns_get_parent_entry (entry_to_search); + } + + name_buffer[--size] = AML_ROOT_PREFIX; + + + return (name_buffer); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_name_of_current_scope + * + * PARAMETERS: none + * + * RETURN: pointer to storage containing the name of the current scope + * + ***************************************************************************/ + +char * +acpi_ns_name_of_current_scope ( + ACPI_WALK_STATE *walk_state) +{ + char *scope_name; + + + if (walk_state && walk_state->scope_info) { + scope_name = + acpi_ns_name_of_scope (walk_state->scope_info->scope.name_table); + + return (scope_name); + } + + REPORT_ERROR ("Current scope pointer is invalid"); + + return (NULL); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_handle_to_pathname + * + * PARAMETERS: Target_handle - Handle of nte whose name is to be found + * Buf_size - Size of the buffer provided + * User_buffer - Where the pathname is returned + * + * RETURN: Status, Buffer is filled with pathname if status == AE_OK + * + * DESCRIPTION: Build and return a full namespace pathname + * + * MUTEX: Locks Namespace + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_handle_to_pathname ( + ACPI_HANDLE target_handle, + u32 *buf_size, + char *user_buffer) +{ + ACPI_STATUS status = AE_OK; + ACPI_NAMED_OBJECT *entry_to_search = NULL; + ACPI_NAMED_OBJECT *temp = NULL; + ACPI_SIZE path_length = 0; + ACPI_SIZE size; + u32 user_buf_size; + ACPI_NAME name; + u8 namespace_was_locked; + + + if (!acpi_gbl_root_object->child_table || !target_handle) { + /* + * If the name space has not been initialized, + * this function should not have been called. + */ + + return (AE_NO_NAMESPACE); + } + + namespace_was_locked = acpi_gbl_acpi_mutex_info[ACPI_MTX_NAMESPACE].locked; + if (!namespace_was_locked) { + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + } + + entry_to_search = acpi_ns_convert_handle_to_entry (target_handle); + if (!entry_to_search) { + return (AE_BAD_PARAMETER); + } + + /* + * Compute length of pathname as 5 * number of name segments. + * Go back up the parent tree to the root + */ + for (size = 0, temp = entry_to_search; + acpi_ns_get_parent_entry (temp); + temp = acpi_ns_get_parent_entry (temp)) + { + size += PATH_SEGMENT_LENGTH; + } + + /* Set return length to the required path length */ + + path_length = size + 1; + user_buf_size = *buf_size; + *buf_size = path_length; + + /* Check if the user buffer is sufficiently large */ + + if (path_length > user_buf_size) { + status = AE_BUFFER_OVERFLOW; + goto unlock_and_exit; + } + + /* Store null terminator */ + + user_buffer[size] = 0; + size -= ACPI_NAME_SIZE; + + /* Put the original ACPI name at the end of the path */ + + MOVE_UNALIGNED32_TO_32 ((user_buffer + size), + &entry_to_search->name); + + user_buffer[--size] = PATH_SEPARATOR; + + /* Build name backwards, putting "." between segments */ + + while ((size > ACPI_NAME_SIZE) && entry_to_search) { + size -= ACPI_NAME_SIZE; + name = acpi_ns_find_parent_name (entry_to_search); + MOVE_UNALIGNED32_TO_32 ((user_buffer + size), &name); + + user_buffer[--size] = PATH_SEPARATOR; + entry_to_search = acpi_ns_get_parent_entry (entry_to_search); + } + + /* + * Overlay the "." preceding the first segment with + * the root name "\" + */ + + user_buffer[size] = '\\'; + + +unlock_and_exit: + + if (!namespace_was_locked) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + } + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_pattern_match + * + * PARAMETERS: Obj_entry - A namespace entry + * Search_for - Wildcard pattern string + * + * DESCRIPTION: Matches a namespace name against a wildcard pattern. Only + * a very simple pattern - 4 chars, either a valid char or a "?" + * to match any. + * + ***************************************************************************/ + +u8 +acpi_ns_pattern_match ( + ACPI_NAMED_OBJECT *obj_entry, + char *search_for) +{ + s32 i; + + + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (search_for[i] != '?' && + search_for[i] != ((char *) &obj_entry->name)[i]) + { + /* No match */ + + return FALSE; + } + } + + /* name matches pattern */ + + return TRUE; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_name_compare + * + * PARAMETERS: Obj_handle - A namespace entry + * Level - Current nesting level + * Context - A FIND_CONTEXT structure + * + * DESCRIPTION: A User_function called by Acpi_ns_walk_namespace(). It performs + * a pattern match for Acpi_ns_low_find_names(), and updates the list + * and count as required. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_name_compare ( + ACPI_HANDLE obj_handle, + u32 level, + void *context, + void **return_value) +{ + FIND_CONTEXT *find = context; + + + /* Match, yes or no? */ + + if (acpi_ns_pattern_match ((ACPI_NAMED_OBJECT*) obj_handle, + find->search_for)) + { + /* Name matches pattern */ + + if (find->list) { + find->list[*(find->count)] = obj_handle; + } + + ++*(find->count); + } + + /* Don't terminate the walk */ + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_low_find_names + * + * PARAMETERS: *This_entry - Table to be searched + * *Search_for - Pattern to be found. + * 4 bytes, ? matches any character. + * *Count - Output count of matches found. + * Outermost caller should preset to 0 + * List[] - Output array of handles. If + * null, only the count is obtained. + * Max_depth - Maximum depth of search. Use + * INT_MAX for an effectively + * unlimited depth. + * + * DESCRIPTION: Low-level find name. + * Traverse the name space finding names which match a search + * pattern, and return an array of handles in List[]. + * + ***************************************************************************/ + +void +acpi_ns_low_find_names ( + ACPI_NAMED_OBJECT *this_entry, + char *search_for, + s32 *count, + ACPI_HANDLE list[], + s32 max_depth) +{ + FIND_CONTEXT find; + + + if (0 == max_depth || !this_entry || !search_for || !count) { + /* + * Zero requested depth, nothing to search, + * nothing to search for, or count pointer bad + */ + + return; + } + + /* Init the context structure used by compare routine */ + + find.list = list; + find.count = count; + find.search_for = search_for; + + /* Walk the namespace and find all matches */ + + acpi_ns_walk_namespace (ACPI_TYPE_ANY, (ACPI_HANDLE) this_entry, + max_depth, NS_WALK_NO_UNLOCK, + acpi_ns_name_compare, &find, NULL); + + if (list) { + /* null-terminate the output array */ + + list[*count] = (ACPI_HANDLE) 0; + } + + return; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_find_names + + * + * PARAMETERS: *Search_for - pattern to be found. + * 4 bytes, ? matches any character. + * If NULL, "????" will be used. + * Start_handle - Root of subtree to be searched, or + * NS_ALL to search the entire namespace + * Max_depth - Maximum depth of search. Use INT_MAX + * for an effectively unlimited depth. + * + * DESCRIPTION: Traverse the name space finding names which match a search + * pattern, and return an array of handles. The end of the + * array is marked by the value (ACPI_HANDLE)0. A return value + * of (ACPI_HANDLE *)0 indicates that no matching names were + * found or that space for the list could not be allocated. + * if Start_handle is NS_ALL (null) search from the root, + * else it is a handle whose children are to be searched. + * + ***************************************************************************/ + +ACPI_HANDLE * +acpi_ns_find_names ( + char *search_for, + ACPI_HANDLE start_handle, + s32 max_depth) +{ + ACPI_HANDLE *list = NULL; + s32 count; + + + if (!acpi_gbl_root_object->child_table) { + /* + * If the name space has not been initialized, + * there surely are no matching names. + */ + return (NULL); + } + + if (NS_ALL == start_handle) { + /* base is root */ + + start_handle = acpi_gbl_root_object; + } + + else if (((ACPI_NAMED_OBJECT *) start_handle)->child_table) { + /* base has children to search */ + + start_handle = + ((ACPI_NAMED_OBJECT *) start_handle)->child_table->entries; + } + + else { + /* + * If base is not the root and has no children, + * there is nothing to search. + */ + return (NULL); + } + + if (!search_for) { + /* Search name not specified */ + + search_for = "????"; + } + + + /* Pass 1. Get required buffer size, don't try to build list */ + + count = 0; + acpi_ns_low_find_names (start_handle, search_for, &count, + NULL, max_depth); + + if (0 == count) { + return (NULL); + } + + /* Allow for trailing null */ + count++; + + list = acpi_cm_callocate (count * sizeof(ACPI_HANDLE)); + if (!list) { + REPORT_ERROR ("Ns_find_names: allocation failure"); + return (NULL); + } + + /* Pass 2. Fill buffer */ + + count = 0; + acpi_ns_low_find_names (start_handle, search_for, &count, list, max_depth); + + return (list); +} + + diff --git a/drivers/acpi/namespace/nsobject.c b/drivers/acpi/namespace/nsobject.c new file mode 100644 index 000000000..56e497bb0 --- /dev/null +++ b/drivers/acpi/namespace/nsobject.c @@ -0,0 +1,556 @@ + +/****************************************************************************** + * + * Module Name: nsobject - Utilities for objects attached to namespace + * table entries + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "namesp.h" +#include "interp.h" +#include "tables.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsobject"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_attach_object + * + * PARAMETERS: Handle - Handle of nte + * Object - Object to be attached + * Type - Type of object, or ACPI_TYPE_ANY if not + * known + * + * DESCRIPTION: Record the given object as the value associated with the + * name whose ACPI_HANDLE is passed. If Object is NULL + * and Type is ACPI_TYPE_ANY, set the name as having no value. + * + * MUTEX: Assumes namespace is locked + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_attach_object ( + ACPI_HANDLE handle, + ACPI_HANDLE object, + OBJECT_TYPE_INTERNAL type) +{ + ACPI_NAMED_OBJECT *this_entry = (ACPI_NAMED_OBJECT*) handle; + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *previous_obj_desc; + OBJECT_TYPE_INTERNAL obj_type = ACPI_TYPE_ANY; + u8 flags; + u16 opcode; + + + /* + * Parameter validation + */ + + if (!acpi_gbl_root_object->child_table) { + /* Name space not initialized */ + + REPORT_ERROR ("Ns_attach_object: Name space not initialized"); + return (AE_NO_NAMESPACE); + } + + if (!handle) { + /* Invalid handle */ + + REPORT_ERROR ("Ns_attach_object: Null name handle"); + return (AE_BAD_PARAMETER); + } + + if (!object && (ACPI_TYPE_ANY != type)) { + /* Null object */ + + REPORT_ERROR ("Ns_attach_object: Null object, but type" + "not ACPI_TYPE_ANY"); + return (AE_BAD_PARAMETER); + } + + if (!VALID_DESCRIPTOR_TYPE (handle, ACPI_DESC_TYPE_NAMED)) { + /* Not a name handle */ + + REPORT_ERROR ("Ns_attach_object: Invalid handle"); + return (AE_BAD_PARAMETER); + } + + /* Check if this object is already attached */ + + if (this_entry->object == object) { + return (AE_OK); + } + + + /* Get the current flags field of the NTE */ + + flags = this_entry->flags; + flags &= ~NTE_AML_ATTACHMENT; + + + /* If null object, we will just install it */ + + if (!object) { + obj_desc = NULL; + obj_type = ACPI_TYPE_ANY; + } + + /* + * If the object is an NTE with an attached object, + * we will use that (attached) object + */ + + else if (VALID_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_NAMED) && + ((ACPI_NAMED_OBJECT*) object)->object) + { + /* + * Value passed is a name handle and that name has a + * non-null value. Use that name's value and type. + */ + + obj_desc = ((ACPI_NAMED_OBJECT*) object)->object; + obj_type = ((ACPI_NAMED_OBJECT*) object)->type; + + /* + * Copy appropriate flags + */ + + if (((ACPI_NAMED_OBJECT*) object)->flags & NTE_AML_ATTACHMENT) { + flags |= NTE_AML_ATTACHMENT; + } + } + + + /* + * Otherwise, we will use the parameter object, but we must type + * it first + */ + + else { + obj_desc = (ACPI_OBJECT_INTERNAL *) object; + + + /* If a valid type (non-ANY) was given, just use it */ + + if (ACPI_TYPE_ANY != type) { + obj_type = type; + } + + + /* + * Type is TYPE_Any, we must try to determinte the + * actual type of the object + */ + + /* + * Check if value points into the AML code + */ + else if (acpi_tb_system_table_pointer (object)) { + /* + * Object points into the AML stream. + * Set a flag bit in the NTE to indicate this + */ + + flags |= NTE_AML_ATTACHMENT; + + /* + * The next byte (perhaps the next two bytes) + * will be the AML opcode + */ + + MOVE_UNALIGNED16_TO_16 (&opcode, object); + + /* Check for a recognized Op_code */ + + switch ((u8) opcode) + { + + case AML_OP_PREFIX: + + if (opcode != AML_REVISION_OP) { + /* + * Op_prefix is unrecognized unless part + * of Revision_op + */ + + break; + } + + /* Else fall through to set type as Number */ + + + case AML_ZERO_OP: case AML_ONES_OP: case AML_ONE_OP: + case AML_BYTE_OP: case AML_WORD_OP: case AML_DWORD_OP: + + obj_type = ACPI_TYPE_NUMBER; + break; + + + case AML_STRING_OP: + + obj_type = ACPI_TYPE_STRING; + break; + + + case AML_BUFFER_OP: + + obj_type = ACPI_TYPE_BUFFER; + break; + + + case AML_MUTEX_OP: + + obj_type = ACPI_TYPE_MUTEX; + break; + + + case AML_PACKAGE_OP: + + obj_type = ACPI_TYPE_PACKAGE; + break; + + + default: + + return (AE_TYPE); + break; + } + } + + else { + /* + * Cannot figure out the type -- set to Def_any which + * will print as an error in the name table dump + */ + + + obj_type = INTERNAL_TYPE_DEF_ANY; + } + } + + + /* + * Must increment the new value's reference count + * (if it is an internal object) + */ + + acpi_cm_add_reference (obj_desc); + + /* Save the existing object (if any) for deletion later */ + + previous_obj_desc = this_entry->object; + + /* Install the object and set the type, flags */ + + this_entry->object = obj_desc; + this_entry->type = (u8) obj_type; + this_entry->flags = flags; + + + /* + * Delete an existing attached object. + */ + + if (previous_obj_desc) { + /* One for the attach to the NTE */ + acpi_cm_remove_reference (previous_obj_desc); + /* Now delete */ + acpi_cm_remove_reference (previous_obj_desc); + } + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_attach_method + * + * PARAMETERS: Handle - Handle of nte to be set + * Offset - Value to be set + * Length - Length associated with value + * + * DESCRIPTION: Record the given offset and p-code length of the method + * whose handle is passed + * + * MUTEX: Assumes namespace is locked + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_attach_method ( + ACPI_HANDLE handle, + u8 *pcode_addr, + u32 pcode_length) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_OBJECT_INTERNAL *previous_obj_desc; + ACPI_NAMED_OBJECT *this_entry = (ACPI_NAMED_OBJECT*) handle; + + + /* Parameter validation */ + + if (!acpi_gbl_root_object->child_table) { + /* Name space uninitialized */ + + REPORT_ERROR ("Ns_attach_method: name space uninitialized"); + return (AE_NO_NAMESPACE); + } + + if (!handle) { + /* Null name handle */ + + REPORT_ERROR ("Ns_attach_method: null name handle"); + return (AE_BAD_PARAMETER); + } + + + /* Allocate a method descriptor */ + + obj_desc = acpi_cm_create_internal_object (ACPI_TYPE_METHOD); + if (!obj_desc) { + /* Method allocation failure */ + + REPORT_ERROR ("Ns_attach_method: allocation failure"); + return (AE_NO_MEMORY); + } + + /* Init the method info */ + + obj_desc->method.pcode = pcode_addr; + obj_desc->method.pcode_length = pcode_length; + + /* Update reference count and install */ + + acpi_cm_add_reference (obj_desc); + + previous_obj_desc = this_entry->object; + this_entry->object = obj_desc; + + + /* + * Delete an existing object. Don't try to re-use in case it is shared + */ + if (previous_obj_desc) { + acpi_cm_remove_reference (previous_obj_desc); + } + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_detach_object + * + * PARAMETERS: Object - An object whose Value will be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete the Value associated with a namespace object. If the + * Value is an allocated object, it is freed. Otherwise, the + * field is simply cleared. + * + ***************************************************************************/ + +void +acpi_ns_detach_object ( + ACPI_HANDLE object) +{ + ACPI_NAMED_OBJECT *entry = object; + ACPI_OBJECT_INTERNAL *obj_desc; + + + obj_desc = entry->object; + if (!obj_desc) { + return; + } + + /* Clear the entry in all cases */ + + entry->object = NULL; + + /* Found a valid value */ + + /* + * Not every value is an object allocated via Acpi_cm_callocate, + * - must check + */ + + if (!acpi_tb_system_table_pointer (obj_desc)) { + /* Attempt to delete the object (and all subobjects) */ + + acpi_cm_remove_reference (obj_desc); + } + + return; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_attached_object + * + * PARAMETERS: Handle - Handle of nte to be examined + * + * RETURN: Current value of the object field from nte whose handle is + * passed + * + ***************************************************************************/ + +void * +acpi_ns_get_attached_object ( + ACPI_HANDLE handle) +{ + + if (!handle) { + /* handle invalid */ + + REPORT_WARNING ("Ns_get_attached_object: Null handle"); + return (NULL); + } + + return (((ACPI_NAMED_OBJECT*) handle)->object); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_compare_object + * + * PARAMETERS: Obj_handle - A namespace entry + * Level - Current nesting level + * Obj_desc - The value to be compared + * + * DESCRIPTION: A User_function called by Acpi_ns_walk_namespace(). It performs + * a comparison for Acpi_ns_find_attached_object(). The comparison is against + * the value in the value field of the Obj_handle (an NTE). + * If a match is found, the handle is returned, which aborts + * Acpi_ns_walk_namespace. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_compare_object ( + ACPI_HANDLE obj_handle, + u32 level, + void *obj_desc, + void **return_value) +{ + + if (((ACPI_NAMED_OBJECT*) obj_handle)->object == obj_desc) { + if (return_value) { + *return_value = obj_handle; + } + + /* Stop the walk */ + return AE_CTRL_TERMINATE; + } + + /* Not found, continue the walk */ + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_find_attached_object + * + * PARAMETERS: *Obj_desc - Value to be found in ptr_val field. + * Start_handle - Root of subtree to be searched, or + * NS_ALL to search the entire namespace + * Max_depth - Maximum depth of search. Use INT_MAX + * for an effectively unlimited depth. + * + * DESCRIPTION: Traverse the name space until finding a name whose Value field + * matches the Obj_desc parameter, and return a handle to that + * name, or (ACPI_HANDLE)0 if none exists. + * if Start_handle is NS_ALL (null) search from the root, + * else it is a handle whose children are to be searched. + * + ***************************************************************************/ + +ACPI_HANDLE +acpi_ns_find_attached_object ( + ACPI_OBJECT_INTERNAL *obj_desc, + ACPI_HANDLE start_handle, + s32 max_depth) +{ + ACPI_HANDLE ret_object; + ACPI_STATUS status; + + + /* Parameter validation */ + + if (!obj_desc) { + return (NULL); + } + + if (0 == max_depth) { + return (NULL); + } + + if (!acpi_gbl_root_object->child_table) { + /* + * If the name space has not been initialized, + * there surely are no matching values. + */ + return (NULL); + } + + if (NS_ALL == start_handle) { + start_handle = acpi_gbl_root_object; + } + + else { + /* + * If base is not the root and has no children, + * there is nothing to search. + */ + return (NULL); + } + + + /* + * Walk namespace until a match is found. + * Either the matching object is returned, or NULL in case + * of no match. + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, start_handle, + max_depth, NS_WALK_NO_UNLOCK, + acpi_ns_compare_object, + obj_desc, &ret_object); + + if (ACPI_FAILURE (status)) { + ret_object = NULL; + } + + return (ret_object); +} + + diff --git a/drivers/acpi/namespace/nssearch.c b/drivers/acpi/namespace/nssearch.c new file mode 100644 index 000000000..19fad8ad6 --- /dev/null +++ b/drivers/acpi/namespace/nssearch.c @@ -0,0 +1,646 @@ + +/****************************************************************************** + * + * Module Name: nssearch - Namespace search + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nssearch"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_search_one_scope + * + * PARAMETERS: *Entry_name - Ascii ACPI name to search for + * *Name_table - Starting table where search will begin + * Type - Object type to match + * **Ret_entry - Where the matched NTE is returned + * *Ret_info - Where info about the search is returned + * + * RETURN: Status and return information via NS_SEARCH_DATA + * + * DESCRIPTION: Search a single namespace table. Performs a simple search, + * does not add entries or search parents. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_search_one_scope ( + u32 entry_name, + ACPI_NAME_TABLE *name_table, + OBJECT_TYPE_INTERNAL type, + ACPI_NAMED_OBJECT **ret_entry, + NS_SEARCH_DATA *ret_info) +{ + u32 position; + ACPI_NAME_TABLE *this_table; + ACPI_NAME_TABLE *previous_table = name_table; + ACPI_NAMED_OBJECT *entries; + u8 table_full = TRUE; + ACPI_NAME_TABLE *table_with_empty_slots = NULL; + u32 empty_slot_position = 0; + + + + /* + * Name tables are built (and subsequently dumped) in the + * order in which the names are encountered during the namespace load; + * + * All namespace searching will be linear; If a table overflows an + * additional segment will be allocated and added (chained). + * + * Start linear search at top of table + */ + position = 0; + this_table = name_table; + entries = this_table->entries; + + + /* Init return data */ + + if (ret_info) { + ret_info->name_table = this_table; + } + + + /* + * Search entire name table, including all linked appendages + */ + + while (this_table) { + /* + * Search for name in table, starting at Position. Stop + * searching upon examining all entries in the table. + * + */ + + entries = this_table->entries; + while (position < NS_TABLE_SIZE) { + /* Check for a valid entry */ + + if (!entries[position].name) { + if (table_full) { + /* + * There is room in the table for more + * entries, if necessary + */ + + table_full = FALSE; + table_with_empty_slots = this_table; + empty_slot_position = position; + } + } + + /* Search for name in table */ + + else if (entries[position].name == entry_name) { + /* + * Found matching entry. Capture type if + * appropriate before returning the entry. + */ + + /* + * The Def_field_defn and Bank_field_defn cases + * are actually looking up the Region in which + * the field will be defined + */ + + if ((INTERNAL_TYPE_DEF_FIELD_DEFN == type) || + (INTERNAL_TYPE_BANK_FIELD_DEFN == type)) + { + type = ACPI_TYPE_REGION; + } + + /* + * Scope, Def_any, and Index_field_defn are bogus + * "types" which do not actually have anything + * to do with the type of the name being looked + * up. For any other value of Type, if the type + * stored in the entry is Any (i.e. unknown), + * save the actual type. + */ + + if (type != INTERNAL_TYPE_SCOPE && + type != INTERNAL_TYPE_DEF_ANY && + type != INTERNAL_TYPE_INDEX_FIELD_DEFN && + entries[position].type == ACPI_TYPE_ANY) + { + entries[position].type = (u8) type; + } + + *ret_entry = &entries[position]; + return (AE_OK); + } + + + /* Didn't match name, move on to the next entry */ + + position++; + } + + + /* + * Just examined last slot in this table, move on + * to next appendate. + * All appendages, even to the root NT, contain + * NS_TABLE_SIZE entries. + */ + + previous_table = this_table; + this_table = this_table->next_table; + + position = 0; + } + + + /* Searched entire table, not found */ + + + if (ret_info) { + /* + * Save info on if/where a slot is available + * (name was not found) + */ + + ret_info->table_full = table_full; + if (table_full) { + ret_info->name_table = previous_table; + } + + else { + ret_info->position = empty_slot_position; + ret_info->name_table = table_with_empty_slots; + } + } + + return (AE_NOT_FOUND); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_search_parent_tree + * + * PARAMETERS: *Entry_name - Ascii ACPI name to search for + * *Name_table - Starting table where search will begin + * Type - Object type to match + * **Ret_entry - Where the matched NTE is returned + * + * RETURN: Status + * + * DESCRIPTION: Called when a name has not been found in the current namespace + * table. Before adding it or giving up, ACPI scope rules require + * searching enclosing scopes in cases identified by Acpi_ns_local(). + * + * "A name is located by finding the matching name in the current + * name space, and then in the parent name space. If the parent + * name space does not contain the name, the search continues + * recursively until either the name is found or the name space + * does not have a parent (the root of the name space). This + * indicates that the name is not found" (From ACPI Specification, + * section 5.3) + * + ***************************************************************************/ + + +ACPI_STATUS +acpi_ns_search_parent_tree ( + u32 entry_name, + ACPI_NAME_TABLE *name_table, + OBJECT_TYPE_INTERNAL type, + ACPI_NAMED_OBJECT **ret_entry) +{ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *parent_entry; + ACPI_NAMED_OBJECT *entries; + + + entries = name_table->entries; + + /* + * If no parent or type is "local", we won't be searching the + * parent tree. + */ + + if (!acpi_ns_local (type) && + name_table->parent_entry) + { + parent_entry = name_table->parent_entry; + /* + * Search parents until found or we have backed up to + * the root + */ + + while (parent_entry) { + /* Search parent scope */ + /* TBD: [Investigate] Why ACPI_TYPE_ANY? */ + + status = acpi_ns_search_one_scope (entry_name, + parent_entry->child_table, + ACPI_TYPE_ANY, + ret_entry, NULL); + + if (status == AE_OK) { + return (status); + } + + /* + * Not found here, go up another level + * (until we reach the root) + */ + + parent_entry = acpi_ns_get_parent_entry (parent_entry); + } + + /* Not found in parent tree */ + } + + + return (AE_NOT_FOUND); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_create_and_link_new_table + * + * PARAMETERS: *Name_table - The table that is to be "extended" by + * the creation of an appendage table. + * + * RETURN: Status + * + * DESCRIPTION: Allocate a new namespace table, initialize it, and link it + * into the parent table. + * + * NOTE: We are in the first or second pass load mode, want to + * add a new table entry, and the current table is full. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_create_and_link_new_table ( + ACPI_NAME_TABLE *name_table) +{ + ACPI_NAME_TABLE *new_table; + ACPI_NAMED_OBJECT *parent_entry; + ACPI_STATUS status = AE_OK; + + + /* Sanity check on the data structure */ + + if (name_table->next_table) { + /* We should never get here (an appendage already allocated) */ + + return (AE_AML_INTERNAL); + } + + + /* + * We can use the parent entries from the current table + * Since the parent information remains the same. + */ + parent_entry = name_table->parent_entry; + + + /* Allocate and chain an appendage to the filled table */ + + new_table = acpi_ns_allocate_name_table (NS_TABLE_SIZE); + if (!new_table) { + REPORT_ERROR ("Name Table appendage allocation failure"); + return (AE_NO_MEMORY); + } + + /* + * Allocation successful. Init the new table. + */ + name_table->next_table = new_table; + acpi_ns_initialize_table (new_table, parent_entry->child_table, + parent_entry); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_initialize_table + * + * PARAMETERS: New_table - The new table to be initialized + * Parent_table - The parent (owner) scope + * Parent_entry - The NTE for the parent + * + * RETURN: None + * + * DESCRIPTION: Initialize a new namespace table. Simple, but called + * from several places -- code should be kept in one place. + * + ***************************************************************************/ + +void +acpi_ns_initialize_table ( + ACPI_NAME_TABLE *new_table, + ACPI_NAME_TABLE *parent_table, + ACPI_NAMED_OBJECT *parent_entry) +{ + u8 i; + + + new_table->parent_entry = parent_entry; + new_table->parent_table = parent_table; + + + /* Init each named object entry in the table */ + + for (i = 0; i < NS_TABLE_SIZE; i++) { + new_table->entries[i].this_index = i; + new_table->entries[i].data_type = ACPI_DESC_TYPE_NAMED; + } + +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_initialize_entry + * + * PARAMETERS: Name_table - The containing table for the new NTE + * Position - Position (index) of the new NTE in the table + * Entry_name - ACPI name of the new entry + * Type - ACPI object type of the new entry + * Previous_entry - Link back to the previous entry (can span + * multiple tables) + * + * RETURN: None + * + * DESCRIPTION: Initialize a new entry within a namespace table. + * + ***************************************************************************/ + +void +acpi_ns_initialize_entry ( + ACPI_WALK_STATE *walk_state, + ACPI_NAME_TABLE *name_table, + u32 position, + u32 entry_name, + OBJECT_TYPE_INTERNAL type) +{ + ACPI_NAMED_OBJECT *new_entry; + u16 owner_id = TABLE_ID_DSDT; + ACPI_NAMED_OBJECT *entries; + + + /* + * Get the owner ID from the Walk state + * The owner ID is used to track table deletion and + * deletion of objects created by methods + */ + if (walk_state) { + owner_id = walk_state->owner_id; + } + + /* The new entry is given by two parameters */ + + entries = name_table->entries; + new_entry = &entries[position]; + + /* Init the new entry */ + + new_entry->data_type = ACPI_DESC_TYPE_NAMED; + new_entry->name = entry_name; + new_entry->owner_id = owner_id; + new_entry->reference_count = 1; + + + /* + * If adding a name with unknown type, or having to + * add the region in order to define fields in it, we + * have a forward reference. + */ + + if ((ACPI_TYPE_ANY == type) || + (INTERNAL_TYPE_DEF_FIELD_DEFN == type) || + (INTERNAL_TYPE_BANK_FIELD_DEFN == type)) + { + /* + * We don't want to abort here, however! + * We will fill in the actual type when the + * real definition is found later. + */ + + } + + /* + * The Def_field_defn and Bank_field_defn cases are actually + * looking up the Region in which the field will be defined + */ + + if ((INTERNAL_TYPE_DEF_FIELD_DEFN == type) || + (INTERNAL_TYPE_BANK_FIELD_DEFN == type)) + { + type = ACPI_TYPE_REGION; + } + + /* + * Scope, Def_any, and Index_field_defn are bogus "types" which do + * not actually have anything to do with the type of the name + * being looked up. Save any other value of Type as the type of + * the entry. + */ + + if ((type != INTERNAL_TYPE_SCOPE) && + (type != INTERNAL_TYPE_DEF_ANY) && + (type != INTERNAL_TYPE_INDEX_FIELD_DEFN)) + { + new_entry->type = (u8) type; + } + + return; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_search_and_enter + * + * PARAMETERS: Entry_name - Ascii ACPI name to search for (4 chars) + * *Name_table - Starting table where search will begin + * Interpreter_mode - Add names only in MODE_Load_pass_x. Otherwise, + * search only. + * Type - Object type to match + * **Ret_entry - Where the matched NTE is returned + * + * RETURN: Status + * + * DESCRIPTION: Search for a name segment in a single name table, + * optionally adding it if it is not found. If the passed + * Type is not Any and the type previously stored in the + * entry was Any (i.e. unknown), update the stored type. + * + * In IMODE_EXECUTE, search only. + * In other modes, search and add if not found. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_search_and_enter ( + u32 entry_name, + ACPI_WALK_STATE *walk_state, + ACPI_NAME_TABLE *name_table, + OPERATING_MODE interpreter_mode, + OBJECT_TYPE_INTERNAL type, + u32 flags, + ACPI_NAMED_OBJECT **ret_entry) +{ + u32 position; /* position in table */ + ACPI_STATUS status; + NS_SEARCH_DATA search_info; + ACPI_NAMED_OBJECT *entry; + ACPI_NAMED_OBJECT *entries; + + + /* Parameter validation */ + + if (!name_table || !entry_name || !ret_entry) { + REPORT_ERROR ("Ns_search_and_enter: bad parameter"); + return (AE_BAD_PARAMETER); + } + + + /* Name must consist of printable characters */ + + if (!acpi_cm_valid_acpi_name (entry_name)) { + return (AE_BAD_CHARACTER); + } + + + /* Try to find the name in the table specified by the caller */ + + *ret_entry = ENTRY_NOT_FOUND; + status = acpi_ns_search_one_scope (entry_name, name_table, + type, ret_entry, &search_info); + if (status != AE_NOT_FOUND) { + /* + * Either found it or there was an error + * -- finished either way + */ + + return (status); + } + + + /* + * Not found in the table. If we are NOT performing the + * first pass (name entry) of loading the namespace, search + * the parent tree (all the way to the root if necessary.) + * We don't want to perform the parent search when the + * namespace is actually being loaded. We want to perform + * the search when namespace references are being resolved + * (load pass 2) and during the execution phase. + */ + + if ((interpreter_mode != IMODE_LOAD_PASS1) && + (flags & NS_SEARCH_PARENT)) + { + /* + * Not found in table - search parent tree according + * to ACPI specification + */ + + status = acpi_ns_search_parent_tree (entry_name, name_table, + type, ret_entry); + + if (status == AE_OK) { + return (status); + } + } + + + /* + * In execute mode, just search, never add names. Exit now. + */ + + if (interpreter_mode == IMODE_EXECUTE) { + return (AE_NOT_FOUND); + } + + + /* + * Extract the pertinent info from the search result struct. + * Name_table and position might now point to an appendage + */ + name_table = search_info.name_table; + position = search_info.position; + + + /* + * This block handles the case where the existing table is full. + * we must allocate a new table before we can initialize a new entry + */ + + if (search_info.table_full) { + status = acpi_ns_create_and_link_new_table (name_table); + if (status != AE_OK) { + return (status); + } + + /* Point to the first slot in the new table */ + + name_table = name_table->next_table; + position = 0; + } + + + /* + * There is room in the table (or we have just allocated a new one.) + * Initialize the new entry + */ + + acpi_ns_initialize_entry (walk_state, name_table, position, + entry_name, type); + + + entries = name_table->entries; + *ret_entry = &entries[position]; + entry = &entries[position]; + + /* + * Increment the reference count(s) of all parents up to + * the root! + */ + + while (acpi_ns_get_parent_entry (entry)) { + entry = acpi_ns_get_parent_entry (entry); + entry->reference_count++; + } + + + return (AE_OK); +} + diff --git a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/namespace/nsutils.c new file mode 100644 index 000000000..c938ede93 --- /dev/null +++ b/drivers/acpi/namespace/nsutils.c @@ -0,0 +1,886 @@ + +/****************************************************************************** + * + * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing + * parents and siblings and Scope manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "namesp.h" +#include "interp.h" +#include "amlcode.h" +#include "tables.h" + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsutils"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_valid_root_prefix + * + * PARAMETERS: Prefix - Character to be checked + * + * RETURN: TRUE if a valid prefix + * + * DESCRIPTION: Check if a character is a valid ACPI Root prefix + * + ***************************************************************************/ + +u8 +acpi_ns_valid_root_prefix ( + char prefix) +{ + + return ((u8) (prefix == '\\')); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_valid_path_separator + * + * PARAMETERS: Sep - Character to be checked + * + * RETURN: TRUE if a valid path separator + * + * DESCRIPTION: Check if a character is a valid ACPI path separator + * + ***************************************************************************/ + +u8 +acpi_ns_valid_path_separator ( + char sep) +{ + + return ((u8) (sep == '.')); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_type + * + * PARAMETERS: Handle - Handle of nte to be examined + * + * RETURN: Type field from nte whose handle is passed + * + ***************************************************************************/ + +OBJECT_TYPE_INTERNAL +acpi_ns_get_type ( + ACPI_HANDLE handle) +{ + + if (!handle) { + /* Handle invalid */ + + REPORT_WARNING ("Ns_get_type: Null handle"); + return (ACPI_TYPE_ANY); + } + + return (((ACPI_NAMED_OBJECT*) handle)->type); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_local + * + * PARAMETERS: Type - A namespace object type + * + * RETURN: LOCAL if names must be found locally in objects of the + * passed type, 0 if enclosing scopes should be searched + * + ***************************************************************************/ + +s32 +acpi_ns_local ( + OBJECT_TYPE_INTERNAL type) +{ + + if (!acpi_cm_valid_object_type (type)) { + /* type code out of range */ + + REPORT_WARNING ("Ns_local: Invalid Object Type"); + return (NSP_NORMAL); + } + + return ((s32) acpi_gbl_ns_properties[type] & NSP_LOCAL); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_internalize_name + * + * PARAMETERS: *External_name - External representation of name + * **Converted Name - Where to return the resulting + * internal represention of the name + * + * RETURN: Status + * + * DESCRIPTION: Convert an external representation (e.g. "\_PR_.CPU0") + * to internal form (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_internalize_name ( + char *external_name, + char **converted_name) +{ + char *result = NULL; + char *internal_name; + ACPI_SIZE num_segments; + u8 fully_qualified = FALSE; + u32 i; + + + if ((!external_name) || + (*external_name == 0) || + (!converted_name)) + { + return (AE_BAD_PARAMETER); + } + + + /* + * For the internal name, the required length is 4 bytes + * per segment, plus 1 each for Root_prefix, Multi_name_prefix_op, + * segment count, trailing null (which is not really needed, + * but no there's harm in putting it there) + * + * strlen() + 1 covers the first Name_seg, which has no + * path separator + */ + + if (acpi_ns_valid_root_prefix (external_name[0])) { + fully_qualified = TRUE; + external_name++; + } + + + /* + * Determine the number of ACPI name "segments" by counting + * the number of path separators within the string. Start + * with one segment since the segment count is (# separators) + * + 1, and zero separators is ok. + */ + + num_segments = 1; + for (i = 0; external_name[i]; i++) { + if (acpi_ns_valid_path_separator (external_name[i])) { + num_segments++; + } + } + + + /* We need a segment to store the internal version of the name */ + + internal_name = acpi_cm_callocate ((ACPI_NAME_SIZE * num_segments) + 4); + if (!internal_name) { + return (AE_NO_MEMORY); + } + + + /* Setup the correct prefixes, counts, and pointers */ + + if (fully_qualified) { + internal_name[0] = '\\'; + internal_name[1] = AML_MULTI_NAME_PREFIX_OP; + internal_name[2] = (char) num_segments; + result = &internal_name[3]; + } + else { + internal_name[0] = AML_MULTI_NAME_PREFIX_OP; + internal_name[1] = (char) num_segments; + result = &internal_name[2]; + } + + + /* Build the name (minus path separators) */ + + for (; num_segments; num_segments--) { + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (acpi_ns_valid_path_separator (*external_name) || + (*external_name == 0)) + { + /* + * Pad the segment with underscore(s) if + * segment is short + */ + + result[i] = '_'; + } + + else { + /* Convert char to uppercase and save it */ + + result[i] = (char) TOUPPER (*external_name); + external_name++; + } + + } + + /* Now we must have a path separator, or the pathname is bad */ + + if (!acpi_ns_valid_path_separator (*external_name) && + (*external_name != 0)) + { + acpi_cm_free (internal_name); + return (AE_BAD_PARAMETER); + } + + /* Move on the next segment */ + + external_name++; + result += ACPI_NAME_SIZE; + } + + + /* Return the completed name */ + + /* Terminate the string! */ + *result = 0; + *converted_name = internal_name; + + + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_externalize_name + * + * PARAMETERS: *Internal_name - Internal representation of name + * **Converted_name - Where to return the resulting + * external representation of name + * + * RETURN: Status + * + * DESCRIPTION: Convert internal name (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * to its external form (e.g. "\_PR_.CPU0") + * + ****************************************************************************/ + +ACPI_STATUS +acpi_ns_externalize_name ( + u32 internal_name_length, + char *internal_name, + u32 *converted_name_length, + char **converted_name) +{ + u32 prefix_length = 0; + u32 names_index = 0; + u32 names_count = 0; + u32 i = 0; + u32 j = 0; + + if (internal_name_length < 0 || + !internal_name || + !converted_name_length || + !converted_name) + { + return (AE_BAD_PARAMETER); + } + + /* + * Check for a prefix (one '\' | one or more '^'). + */ + switch (internal_name[0]) + { + case '\\': + prefix_length = 1; + break; + + case '^': + for (i = 0; i < internal_name_length; i++) { + if (internal_name[i] != '^') { + prefix_length = i + 1; + } + } + + if (i == internal_name_length) { + prefix_length = i; + } + + break; + } + + /* + * Check for object names. Note that there could be 0-255 of these + * 4-byte elements. + */ + if (prefix_length < internal_name_length) { + switch (internal_name[prefix_length]) + { + + /* <count> 4-byte names */ + + case AML_MULTI_NAME_PREFIX_OP: + names_index = prefix_length + 2; + names_count = (u32) internal_name[prefix_length + 1]; + break; + + + /* two 4-byte names */ + + case AML_DUAL_NAME_PREFIX: + names_index = prefix_length + 1; + names_count = 2; + break; + + + /* Null_name */ + + case 0: + names_index = 0; + names_count = 0; + break; + + + /* one 4-byte name */ + + default: + names_index = prefix_length; + names_count = 1; + break; + } + } + + /* + * Calculate the length of Converted_name, which equals the length + * of the prefix, length of all object names, length of any required + * punctuation ('.') between object names, plus the NULL terminator. + */ + *converted_name_length = prefix_length + (4 * names_count) + + ((names_count > 0) ? (names_count - 1) : 0) + 1; + + /* + * Check to see if we're still in bounds. If not, there's a problem + * with Internal_name (invalid format). + */ + if (*converted_name_length > internal_name_length) { + REPORT_ERROR ("Ns_externalize_name: Invalid internal name.\n"); + return (AE_BAD_PATHNAME); + } + + /* + * Build Converted_name... + */ + + (*converted_name) = acpi_cm_callocate (*converted_name_length); + if (!(*converted_name)) { + return (AE_NO_MEMORY); + } + + j = 0; + + for (i = 0; i < prefix_length; i++) { + (*converted_name)[j++] = internal_name[i]; + } + + if (names_count > 0) { + for (i = 0; i < names_count; i++) { + if (i > 0) { + (*converted_name)[j++] = '.'; + } + + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + } + } + + return (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_convert_handle_to_entry + * + * PARAMETERS: Handle - Handle to be converted to an NTE + * + * RETURN: A Name table entry pointer + * + * DESCRIPTION: Convert a namespace handle to a real NTE + * + ****************************************************************************/ + +ACPI_NAMED_OBJECT* +acpi_ns_convert_handle_to_entry ( + ACPI_HANDLE handle) +{ + + /* + * Simple implementation for now; + * TBD: [Future] Real integer handles allow for more verification + * and keep all pointers within this subsystem! + */ + + if (!handle) { + return NULL; + } + + if (handle == ACPI_ROOT_OBJECT) { + return acpi_gbl_root_object; + } + + + /* We can at least attempt to verify the handle */ + + if (!VALID_DESCRIPTOR_TYPE (handle, ACPI_DESC_TYPE_NAMED)) { + return NULL; + } + + return (ACPI_NAMED_OBJECT*) handle; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_convert_entry_to_handle + * + * PARAMETERS: Nte - NTE to be converted to a Handle + * + * RETURN: An USER ACPI_HANDLE + * + * DESCRIPTION: Convert a real NTE to a namespace handle + * + ****************************************************************************/ + +ACPI_HANDLE +acpi_ns_convert_entry_to_handle(ACPI_NAMED_OBJECT*nte) +{ + + + /* + * Simple implementation for now; + * TBD: [Future] Real integer handles allow for more verification + * and keep all pointers within this subsystem! + */ + + return (ACPI_HANDLE) nte; + + +/* --------------------------------------------------- + + if (!Nte) { + return NULL; + } + + if (Nte == Acpi_gbl_Root_object) { + return ACPI_ROOT_OBJECT; + } + + + return (ACPI_HANDLE) Nte; +------------------------------------------------------*/ +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ns_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free memory allocated for table storage. + * + ******************************************************************************/ + +void +acpi_ns_terminate (void) +{ + ACPI_OBJECT_INTERNAL *obj_desc; + ACPI_NAMED_OBJECT *entry; + + + entry = acpi_gbl_root_object; + + /* + * 1) Free the entire namespace -- all objects, tables, and stacks + */ + /* + * Delete all objects linked to the root + * (additional table descriptors) + */ + + acpi_ns_delete_namespace_subtree (entry); + + /* Detach any object(s) attached to the root */ + + obj_desc = acpi_ns_get_attached_object (entry); + if (obj_desc) { + acpi_ns_detach_object (entry); + acpi_cm_remove_reference (obj_desc); + } + + acpi_ns_delete_name_table (entry->child_table); + entry->child_table = NULL; + + + REPORT_SUCCESS ("Entire namespace and objects deleted"); + + /* + * 2) Now we can delete the ACPI tables + */ + + acpi_tb_delete_acpi_tables (); + + return; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_opens_scope + * + * PARAMETERS: Type - A valid namespace type + * + * RETURN: NEWSCOPE if the passed type "opens a name scope" according + * to the ACPI specification, else 0 + * + ***************************************************************************/ + +s32 +acpi_ns_opens_scope ( + OBJECT_TYPE_INTERNAL type) +{ + + if (!acpi_cm_valid_object_type (type)) { + /* type code out of range */ + + REPORT_WARNING ("Ns_opens_scope: Invalid Object Type"); + return (NSP_NORMAL); + } + + return (((s32) acpi_gbl_ns_properties[type]) & NSP_NEWSCOPE); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_named_object + * + * PARAMETERS: *Pathname - Name to be found, in external (ASL) format. The + * \ (backslash) and ^ (carat) prefixes, and the + * . (period) to separate segments are supported. + * In_scope - Root of subtree to be searched, or NS_ALL for the + * root of the name space. If Name is fully + * qualified (first char is '\'), the passed value + * of Scope will not be accessed. + * Out_nte - Where the Nte is returned + * + * DESCRIPTION: Look up a name relative to a given scope and return the + * corresponding NTE. NOTE: Scope can be null. + * + * MUTEX: Locks namespace + * + ***************************************************************************/ + +ACPI_STATUS +acpi_ns_get_named_object ( + char *pathname, + ACPI_NAME_TABLE *in_scope, + ACPI_NAMED_OBJECT **out_nte) +{ + ACPI_GENERIC_STATE scope_info; + ACPI_STATUS status; + ACPI_NAMED_OBJECT *obj_entry = NULL; + char *internal_path = NULL; + + + scope_info.scope.name_table = in_scope; + + /* Ensure that the namespace has been initialized */ + + if (!acpi_gbl_root_object->child_table) { + return (AE_NO_NAMESPACE); + } + + if (!pathname) { + return (AE_BAD_PARAMETER); + } + + + /* Convert path to internal representation */ + + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return (status); + } + + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* NS_ALL means start from the root */ + + if (NS_ALL == scope_info.scope.name_table) { + scope_info.scope.name_table = acpi_gbl_root_object->child_table; + } + + else { + scope_info.scope.name_table = in_scope; + if (!scope_info.scope.name_table) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* Lookup the name in the namespace */ + + status = acpi_ns_lookup (&scope_info, internal_path, + ACPI_TYPE_ANY, IMODE_EXECUTE, + NS_NO_UPSEARCH | NS_DONT_OPEN_SCOPE, + NULL, &obj_entry); + + + /* Return what was wanted - the NTE that matches the name */ + + *out_nte = obj_entry; + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + /* Cleanup */ + + acpi_cm_free (internal_path); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_find_parent_name + * + * PARAMETERS: *Child_entry - nte whose name is to be found + * + * RETURN: The ACPI name + * + * DESCRIPTION: Search for the given nte in its parent scope and return the + * name segment, or "????" if the parent name can't be found + * (which "should not happen"). + * + ***************************************************************************/ + +ACPI_NAME +acpi_ns_find_parent_name ( + ACPI_NAMED_OBJECT *child_entry) +{ + ACPI_NAMED_OBJECT *parent_entry; + + + if (child_entry) { + /* Valid entry. Get the parent Nte */ + + parent_entry = acpi_ns_get_parent_entry (child_entry); + if (parent_entry) { + if (parent_entry->name) { + return (parent_entry->name); + } + } + + } + + + return (ACPI_UNKNOWN_NAME); +} + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_exist_downstream_sibling + * + * PARAMETERS: *This_entry - pointer to first nte to examine + * + * RETURN: TRUE if sibling is found, FALSE otherwise + * + * DESCRIPTION: Searches remainder of scope being processed to determine + * whether there is a downstream sibling to the current + * object. This function is used to determine what type of + * line drawing character to use when displaying namespace + * trees. + * + ***************************************************************************/ + +u8 +acpi_ns_exist_downstream_sibling ( + ACPI_NAMED_OBJECT *this_entry) +{ + + if (!this_entry) { + return FALSE; + } + + if (this_entry->name) { + return TRUE; + } + + +/* TBD: what did this really do? + if (This_entry->Next_entry) { + return TRUE; + } +*/ + return FALSE; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_owner_table + * + * PARAMETERS: + * + * RETURN: + * + * DESCRIPTION: + * + ***************************************************************************/ + + +ACPI_NAME_TABLE * +acpi_ns_get_owner_table ( + ACPI_NAMED_OBJECT *this_entry) +{ + + /* + * Given an entry in the Name_table->Entries field of a name table, + * we can create a pointer to the beginning of the table as follows: + * + * 1) Starting with the the pointer to the entry, + * 2) Subtract the entry index * size of each entry to get a + * pointer to Entries[0] + * 3) Subtract the size of NAME_TABLE structure to get a pointer + * to the start. + * + * This saves having to put a pointer in every entry that points + * back to the beginning of the table and/or a pointer back to + * the parent. + */ + + return (ACPI_NAME_TABLE *) ((char *) this_entry - + (this_entry->this_index * + sizeof (ACPI_NAMED_OBJECT)) - + (sizeof (ACPI_NAME_TABLE) - + sizeof (ACPI_NAMED_OBJECT))); + +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_parent_entry + * + * PARAMETERS: This_entry - Current table entry + * + * RETURN: Parent entry of the given entry + * + * DESCRIPTION: Obtain the parent entry for a given entry in the namespace. + * + ***************************************************************************/ + + +ACPI_NAMED_OBJECT * +acpi_ns_get_parent_entry ( + ACPI_NAMED_OBJECT *this_entry) +{ + ACPI_NAME_TABLE *name_table; + + + name_table = acpi_ns_get_owner_table (this_entry); + + /* + * Now that we have a pointer to the name table, we can just pluck + * the parent + */ + + return (name_table->parent_entry); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_ns_get_next_valid_entry + * + * PARAMETERS: This_entry - Current table entry + * + * RETURN: Next valid object in the table. NULL if no more valid + * objects + * + * DESCRIPTION: Find the next valid object within a name table. + * + ***************************************************************************/ + + +ACPI_NAMED_OBJECT * +acpi_ns_get_next_valid_entry ( + ACPI_NAMED_OBJECT *this_entry) +{ + ACPI_NAME_TABLE *name_table; + u32 index; + + + index = this_entry->this_index + 1; + name_table = acpi_ns_get_owner_table (this_entry); + + + while (name_table) { + if (index >= NS_TABLE_SIZE) { + /* We are at the end of this table */ + + name_table = name_table->next_table; + index = 0; + continue; + } + + + /* Is this a valid (occupied) slot? */ + + if (name_table->entries[index].name) { + /* Found a valid entry, all done */ + + return (&name_table->entries[index]); + } + + /* Go to the next slot */ + + index++; + } + + /* No more valid entries in this name table */ + + return NULL; +} + + diff --git a/drivers/acpi/namespace/nswalk.c b/drivers/acpi/namespace/nswalk.c new file mode 100644 index 000000000..6fcb14b76 --- /dev/null +++ b/drivers/acpi/namespace/nswalk.c @@ -0,0 +1,279 @@ + +/****************************************************************************** + * + * Module Name: nswalk - Functions for walking the APCI namespace + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nswalk"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_next_object + * + * PARAMETERS: Type - Type of object to be searched for + * Parent - Parent object whose children we are getting + * Last_child - Previous child that was found. + * The NEXT child will be returned + * Ret_handle - Where handle to the next object is placed + * + * RETURN: Status + * + * DESCRIPTION: Return the next peer object within the namespace. If Handle is + * valid, Scope is ignored. Otherwise, the first object within + * Scope is returned. + * + ******************************************************************************/ + +ACPI_NAMED_OBJECT* +acpi_ns_get_next_object ( + OBJECT_TYPE_INTERNAL type, + ACPI_NAMED_OBJECT *parent, + ACPI_NAMED_OBJECT *child) +{ + ACPI_NAMED_OBJECT *this_entry = NULL; + + + if (!child) { + + /* It's really the parent's _scope_ that we want */ + + if (parent->child_table) { + this_entry = parent->child_table->entries; + } + } + + else { + /* Start search at the NEXT object */ + + this_entry = acpi_ns_get_next_valid_entry (child); + } + + + /* If any type is OK, we are done */ + + if (type == ACPI_TYPE_ANY) { + /* Make sure this is valid entry first */ + + if ((!this_entry) || + (!this_entry->name)) + { + return NULL; + } + + return (this_entry); + } + + + /* Must search for the object -- but within this scope only */ + + while (this_entry) { + /* If type matches, we are done */ + + if (this_entry->type == type) { + return (this_entry); + } + + /* Otherwise, move on to the next object */ + + this_entry = acpi_ns_get_next_valid_entry (this_entry); + } + + + /* Not found */ + + return NULL; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_ns_walk_namespace + * + * PARAMETERS: Type - ACPI_OBJECT_TYPE to search for + * Start_object - Handle in namespace where search begins + * Max_depth - Depth to which search is to reach + * Unlock_before_callback- Whether to unlock the NS before invoking + * the callback routine + * User_function - Called when an object of "Type" is found + * Context - Passed to user function + * + * RETURNS Return value from the User_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by Start_handle. + * The User_function is called whenever an object that matches + * the type parameter is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the User Function can be tailored + * to each task, whether it is a print function, a compare + * function, etc. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ns_walk_namespace ( + OBJECT_TYPE_INTERNAL type, + ACPI_HANDLE start_object, + u32 max_depth, + u8 unlock_before_callback, + WALK_CALLBACK user_function, + void *context, + void **return_value) +{ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *child_entry; + ACPI_NAMED_OBJECT *parent_entry; + OBJECT_TYPE_INTERNAL child_type; + u32 level; + + + /* Special case for the namespace root object */ + + if (start_object == ACPI_ROOT_OBJECT) { + start_object = acpi_gbl_root_object; + } + + + /* Null child means "get first object" */ + + parent_entry = start_object; + child_entry = 0; + child_type = ACPI_TYPE_ANY; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up to where we + * started. When Level is zero, the loop is done because we have + * bubbled up to (and passed) the original parent handle (Start_entry) + */ + + while (level > 0) { + /* + * Get the next typed object in this scope. Null returned + * if not found + */ + + status = AE_OK; + child_entry = acpi_ns_get_next_object (ACPI_TYPE_ANY, + parent_entry, + child_entry); + + if (child_entry) { + /* + * Found an object, Get the type if we are not + * searching for ANY + */ + + if (type != ACPI_TYPE_ANY) { + child_type = child_entry->type; + } + + if (child_type == type) { + /* + * Found a matching object, invoke the user + * callback function + */ + + if (unlock_before_callback) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + } + + status = user_function (child_entry, level, + context, return_value); + + if (unlock_before_callback) { + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + } + + switch (status) + { + case AE_OK: + case AE_CTRL_DEPTH: + /* Just keep going */ + break; + + case AE_CTRL_TERMINATE: + /* Exit now, with OK status */ + return (AE_OK); + break; + + default: + /* All others are valid exceptions */ + return (status); + break; + } + } + + /* + * Depth first search: + * Attempt to go down another level in the namespace + * if we are allowed to. Don't go any further if we + * have reached the caller specified maximum depth + * or if the user function has specified that the + * maximum depth has been reached. + */ + + if ((level < max_depth) && (status != AE_CTRL_DEPTH)) { + if (acpi_ns_get_next_object (ACPI_TYPE_ANY, + child_entry, 0)) + { + /* + * There is at least one child of this + * object, visit the object + */ + level++; + parent_entry = child_entry; + child_entry = 0; + } + } + } + + else { + /* + * No more children in this object (Acpi_ns_get_next_object + * failed), go back upwards in the namespace tree to + * the object's parent. + */ + level--; + child_entry = parent_entry; + parent_entry = acpi_ns_get_parent_entry (parent_entry); + } + } + + /* Complete walk, not terminated by user function */ + return (AE_OK); +} + + diff --git a/drivers/acpi/namespace/nsxfname.c b/drivers/acpi/namespace/nsxfname.c new file mode 100644 index 000000000..2622a9be8 --- /dev/null +++ b/drivers/acpi/namespace/nsxfname.c @@ -0,0 +1,372 @@ +/****************************************************************************** + * + * Module Name: nsxfname - Public interfaces to the ACPI subsystem + * ACPI Namespace oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "events.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsxfname"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_load_namespace + * + * PARAMETERS: Display_aml_during_load + * + * RETURN: Status + * + * DESCRIPTION: Load the name space from what ever is pointed to by DSDT. + * (DSDT points to either the BIOS or a buffer.) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_load_namespace ( + void) +{ + ACPI_STATUS status; + + + /* There must be at least a DSDT installed */ + + if (acpi_gbl_DSDT == NULL) { + return (AE_NO_ACPI_TABLES); + } + + + /* Init the hardware */ + + /* + * TBD: [Restructure] Should this should be moved elsewhere, + * like Acpi_enable! ?? + */ + + /* we need to be able to call this interface repeatedly! */ + /* Does H/W require init before loading the namespace? */ + + status = acpi_cm_hardware_initialize (); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Load the namespace. The DSDT is required, + * but the SSDT and PSDT tables are optional. + */ + + status = acpi_ns_load_table_by_type (ACPI_TABLE_DSDT); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Ignore exceptions from these */ + + acpi_ns_load_table_by_type (ACPI_TABLE_SSDT); + acpi_ns_load_table_by_type (ACPI_TABLE_PSDT); + + + /* + * Install the default Op_region handlers, ignore the return + * code right now. + */ + + acpi_ev_install_default_address_space_handlers (); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_handle + * + * PARAMETERS: Parent - Object to search under (search scope). + * Path_name - Pointer to an asciiz string containing the + * name + * Ret_handle - Where the return handle is placed + * + * RETURN: Status + * + * DESCRIPTION: This routine will search for a caller specified name in the + * name space. The caller can restrict the search region by + * specifying a non NULL parent. The parent value is itself a + * namespace handle. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_handle ( + ACPI_HANDLE parent, + ACPI_STRING pathname, + ACPI_HANDLE *ret_handle) +{ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *this_entry; + ACPI_NAME_TABLE *scope = NULL; + + + if (!ret_handle || !pathname) { + return AE_BAD_PARAMETER; + } + + if (parent) { + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + this_entry = acpi_ns_convert_handle_to_entry (parent); + if (!this_entry) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return AE_BAD_PARAMETER; + } + + scope = this_entry->child_table; + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + } + + /* Special case for root, since we can't search for it */ + /* TBD: [Investigate] Check for both forward and backslash?? */ + + if (STRCMP (pathname, NS_ROOT_PATH) == 0) { + *ret_handle = acpi_ns_convert_entry_to_handle (acpi_gbl_root_object); + return AE_OK; + } + + /* + * Find the Nte and convert to the user format + */ + this_entry = NULL; + status = acpi_ns_get_named_object (pathname, scope, &this_entry); + + *ret_handle = acpi_ns_convert_entry_to_handle (this_entry); + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_pathname + * + * PARAMETERS: Handle - Handle to be converted to a pathname + * Name_type - Full pathname or single segment + * Ret_path_ptr - Buffer for returned path + * + * RETURN: Pointer to a string containing the fully qualified Name. + * + * DESCRIPTION: This routine returns the fully qualified name associated with + * the Handle parameter. This and the Acpi_pathname_to_handle are + * complementary functions. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_name ( + ACPI_HANDLE handle, + u32 name_type, + ACPI_BUFFER *ret_path_ptr) +{ + ACPI_STATUS status; + ACPI_NAMED_OBJECT *obj_entry; + + + /* Buffer pointer must be valid always */ + + if (!ret_path_ptr || (name_type > ACPI_NAME_TYPE_MAX)) { + return AE_BAD_PARAMETER; + } + + /* Allow length to be zero and ignore the pointer */ + + if ((ret_path_ptr->length) && + (!ret_path_ptr->pointer)) + { + return AE_BAD_PARAMETER; + } + + if (name_type == ACPI_FULL_PATHNAME) { + /* Get the full pathname (From the namespace root) */ + + status = acpi_ns_handle_to_pathname (handle, &ret_path_ptr->length, + ret_path_ptr->pointer); + return status; + } + + /* + * Wants the single segment ACPI name. + * Validate handle and convert to an NTE + */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + obj_entry = acpi_ns_convert_handle_to_entry (handle); + if (!obj_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Check if name will fit in buffer */ + + if (ret_path_ptr->length < PATH_SEGMENT_LENGTH) { + ret_path_ptr->length = PATH_SEGMENT_LENGTH; + status = AE_BUFFER_OVERFLOW; + goto unlock_and_exit; + } + + /* Just copy the ACPI name from the NTE and zero terminate it */ + + STRNCPY (ret_path_ptr->pointer, (char *) &obj_entry->name, + ACPI_NAME_SIZE); + ((char *) ret_path_ptr->pointer) [ACPI_NAME_SIZE] = 0; + status = AE_OK; + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return status; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_object_info + * + * PARAMETERS: Handle - Object Handle + * Info - Where the info is returned + * + * RETURN: Status + * + * DESCRIPTION: Returns information about an object as gleaned from running + * several standard control methods. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_object_info ( + ACPI_HANDLE device, + ACPI_DEVICE_INFO *info) +{ + DEVICE_ID hid; + DEVICE_ID uid; + ACPI_STATUS status; + u32 device_status = 0; + u32 address = 0; + ACPI_NAMED_OBJECT *device_entry; + + + /* Parameter validation */ + + if (!device || !info) { + return AE_BAD_PARAMETER; + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + device_entry = acpi_ns_convert_handle_to_entry (device); + if (!device_entry) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return AE_BAD_PARAMETER; + } + + info->type = device_entry->type; + info->name = device_entry->name; + info->parent = + acpi_ns_convert_entry_to_handle (acpi_ns_get_parent_entry (device_entry)); + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + /* + * If not a device, we are all done. + */ + if (info->type != ACPI_TYPE_DEVICE) { + return AE_OK; + } + + + /* Get extra info for ACPI devices */ + + info->valid = 0; + + /* Execute the _HID method and save the result */ + + status = acpi_cm_execute_HID (device_entry, &hid); + if (ACPI_SUCCESS (status)) { + if (hid.type == STRING_PTR_DEVICE_ID) { + STRCPY (info->hardware_id, hid.data.string_ptr); + } + else { + STRCPY (info->hardware_id, hid.data.buffer); + } + + info->valid |= ACPI_VALID_HID; + } + + /* Execute the _UID method and save the result */ + + status = acpi_cm_execute_UID (device_entry, &uid); + if (ACPI_SUCCESS (status)) { + if (hid.type == STRING_PTR_DEVICE_ID) { + STRCPY (info->unique_id, uid.data.string_ptr); + } + else { + STRCPY (info->unique_id, uid.data.buffer); + } + + info->valid |= ACPI_VALID_UID; + } + + /* + * Execute the _STA method and save the result + * _STA is not always present + */ + + status = acpi_cm_execute_STA (device_entry, &device_status); + if (ACPI_SUCCESS (status)) { + info->current_status = device_status; + info->valid |= ACPI_VALID_STA; + } + + /* + * Execute the _ADR method and save result if successful + * _ADR is not always present + */ + + status = acpi_cm_evaluate_numeric_object (METHOD_NAME__ADR, + device_entry, &address); + + if (ACPI_SUCCESS (status)) { + info->address = address; + info->valid |= ACPI_VALID_ADR; + } + + return AE_OK; +} + diff --git a/drivers/acpi/namespace/nsxfobj.c b/drivers/acpi/namespace/nsxfobj.c new file mode 100644 index 000000000..09c5e4e3d --- /dev/null +++ b/drivers/acpi/namespace/nsxfobj.c @@ -0,0 +1,554 @@ + +/****************************************************************************** + * + * Module Name: nsxfobj - Public interfaces to the ACPI subsystem + * ACPI Object oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" + + +#define _COMPONENT NAMESPACE + MODULE_NAME ("nsxfobj"); + + +/**************************************************************************** + * + * FUNCTION: Acpi_evaluate_object + * + * PARAMETERS: Handle - Object handle (optional) + * *Pathname - Object pathname (optional) + * **Params - List of parameters to pass to + * method, terminated by NULL. + * Params itself may be NULL + * if no parameters are being + * passed. + * *Return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * + * RETURN: Status + * + * DESCRIPTION: Find and evaluate the given object, passing the given + * parameters if necessary. One of "Handle" or "Pathname" must + * be valid (non-null) + * + ****************************************************************************/ + +ACPI_STATUS +acpi_evaluate_object ( + ACPI_HANDLE handle, + ACPI_STRING pathname, + ACPI_OBJECT_LIST *param_objects, + ACPI_BUFFER *return_buffer) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL **param_ptr = NULL; + ACPI_OBJECT_INTERNAL *return_obj = NULL; + ACPI_OBJECT_INTERNAL *object_ptr = NULL; + u32 buffer_space_needed; + u32 user_buffer_length; + u32 count; + u32 i; + u32 param_length; + u32 object_length; + + + /* + * If there are parameters to be passed to the object + * (which must be a control method), the external objects + * must be converted to internal objects + */ + + if (param_objects && param_objects->count) { + /* + * Allocate a new parameter block for the internal objects + * Add 1 to count to allow for null terminated internal list + * TBD: [Restructure] merge into single allocation! + */ + + count = param_objects->count; + param_length = (count + 1) * sizeof (void *); + object_length = count * sizeof (ACPI_OBJECT_INTERNAL); + + param_ptr = acpi_cm_callocate (param_length + /* Parameter List part */ + object_length); /* Actual objects */ + if (!param_ptr) { + return (AE_NO_MEMORY); + } + + object_ptr = (ACPI_OBJECT_INTERNAL *) ((u8 *) param_ptr + + param_length); + + /* + * Init the param array of pointers and NULL terminate + * the list + */ + + for (i = 0; i < count; i++) { + param_ptr[i] = &object_ptr[i]; + acpi_cm_init_static_object (&object_ptr[i]); + } + param_ptr[count] = NULL; + + /* + * Convert each external object in the list to an + * internal object + */ + for (i = 0; i < count; i++) { + status = + acpi_cm_build_internal_object (¶m_objects->pointer[i], + param_ptr[i]); + + if (ACPI_FAILURE (status)) { + acpi_cm_delete_internal_object_list (param_ptr); + return (status); + } + } + } + + + /* + * Three major cases: + * 1) Fully qualified pathname + * 2) No handle, not fully qualified pathname (error) + * 3) Valid handle + */ + + if ((pathname) && + (acpi_ns_valid_root_prefix (pathname[0]))) + { + /* + * The path is fully qualified, just evaluate by name + */ + status = acpi_ns_evaluate_by_name (pathname, param_ptr, &return_obj); + } + + else if (!handle) { + /* + * A handle is optional iff a fully qualified pathname + * is specified. Since we've already handled fully + * qualified names above, this is an error + */ + + + + status = AE_BAD_PARAMETER; + } + + else { + /* + * We get here if we have a handle -- and if we have a + * pathname it is relative. The handle will be validated + * in the lower procedures + */ + + if (!pathname) { + /* + * The null pathname case means the handle is for + * the actual object to be evaluated + */ + status = acpi_ns_evaluate_by_handle (handle, + param_ptr, + &return_obj); + } + + else { + /* + * Both a Handle and a relative Pathname + */ + status = acpi_ns_evaluate_relative (handle, pathname, + param_ptr, + &return_obj); + } + } + + + /* + * If we are expecting a return value, and all went well above, + * copy the return value to an external object. + */ + + if (return_buffer) { + user_buffer_length = return_buffer->length; + return_buffer->length = 0; + + if (return_obj) { + if (VALID_DESCRIPTOR_TYPE (return_obj, + ACPI_DESC_TYPE_NAMED)) + { + /* + * If we got an NTE as a return object, + * this means the object we are evaluating + * has nothing interesting to return (such + * as a mutex, etc.) We return an error + * because these types are essentially + * unsupported by this interface. We + * don't check up front because this makes + * it easier to add support for various + * types at a later date if necessary. + */ + status = AE_TYPE; + return_obj = NULL; /* No need to delete an NTE */ + } + + if (ACPI_SUCCESS (status)) { + /* + * Find out how large a buffer is needed + * to contain the returned object + */ + status = acpi_cm_get_object_size (return_obj, + &buffer_space_needed); + if (ACPI_SUCCESS (status)) { + /* + * Check if there is enough room in the + * caller's buffer + */ + + if (user_buffer_length < buffer_space_needed) { + /* + * Caller's buffer is too small, can't + * give him partial results fail the call + * but return the buffer size needed + */ + + return_buffer->length = buffer_space_needed; + status = AE_BUFFER_OVERFLOW; + } + + else { + /* + * We have enough space for the object, build it + */ + status = + acpi_cm_build_external_object (return_obj, + return_buffer); + return_buffer->length = buffer_space_needed; + } + } + } + } + } + + + /* Delete the return and parameter objects */ + + if (return_obj) { + /* + * Delete the internal return object. (Or at least + * decrement the reference count by one) + */ + acpi_cm_remove_reference (return_obj); + } + + /* + * Free the input parameter list (if we created one), + */ + + if (param_ptr) { + /* Free the allocated parameter block */ + + acpi_cm_delete_internal_object_list (param_ptr); + } + + return (status); +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_next_object + * + * PARAMETERS: Type - Type of object to be searched for + * Parent - Parent object whose children we are getting + * Last_child - Previous child that was found. + * The NEXT child will be returned + * Ret_handle - Where handle to the next object is placed + * + * RETURN: Status + * + * DESCRIPTION: Return the next peer object within the namespace. If Handle is + * valid, Scope is ignored. Otherwise, the first object within + * Scope is returned. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_next_object ( + ACPI_OBJECT_TYPE type, + ACPI_HANDLE parent, + ACPI_HANDLE child, + ACPI_HANDLE *ret_handle) +{ + ACPI_STATUS status = AE_OK; + ACPI_NAMED_OBJECT *entry; + ACPI_NAMED_OBJECT *parent_entry = NULL; + ACPI_NAMED_OBJECT *child_entry = NULL; + + + /* Parameter validation */ + + if (type > ACPI_TYPE_MAX) { + return AE_BAD_PARAMETER; + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* If null handle, use the parent */ + + if (!child) { + /* Start search at the beginning of the specified scope */ + + parent_entry = acpi_ns_convert_handle_to_entry (parent); + if (!parent_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* Non-null handle, ignore the parent */ + + else { + /* Convert and validate the handle */ + + child_entry = acpi_ns_convert_handle_to_entry (child); + if (!child_entry) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + + /* Internal function does the real work */ + + entry = acpi_ns_get_next_object ((OBJECT_TYPE_INTERNAL) type, + parent_entry, child_entry); + if (!entry) { + status = AE_NOT_FOUND; + goto unlock_and_exit; + } + + if (ret_handle) { + *ret_handle = acpi_ns_convert_entry_to_handle (entry); + } + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return status; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_type + * + * PARAMETERS: Handle - Handle of object whose type is desired + * *Ret_type - Where the type will be placed + * + * RETURN: Status + * + * DESCRIPTION: This routine returns the type associatd with a particular handle + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_type ( + ACPI_HANDLE handle, + ACPI_OBJECT_TYPE *ret_type) +{ + ACPI_NAMED_OBJECT *object; + + + /* Parameter Validation */ + + if (!ret_type) { + return AE_BAD_PARAMETER; + } + + /* + * Special case for the predefined Root Object + * (return type ANY) + */ + + if (handle == ACPI_ROOT_OBJECT) { + *ret_type = ACPI_TYPE_ANY; + return AE_OK; + } + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Convert and validate the handle */ + + object = acpi_ns_convert_handle_to_entry (handle); + if (!object) { + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return AE_BAD_PARAMETER; + } + + *ret_type = object->type; + + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return AE_OK; +} + + +/**************************************************************************** + * + * FUNCTION: Acpi_get_parent + * + * PARAMETERS: Handle - Handle of object whose parent is desired + * Ret_handle - Where the parent handle will be placed + * + * RETURN: Status + * + * DESCRIPTION: Returns a handle to the parent of the object represented by + * Handle. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_parent ( + ACPI_HANDLE handle, + ACPI_HANDLE *ret_handle) +{ + ACPI_NAMED_OBJECT *object; + ACPI_STATUS status = AE_OK; + + + /* No trace macro, too verbose */ + + + if (!ret_handle) { + return AE_BAD_PARAMETER; + } + + /* Special case for the predefined Root Object (no parent) */ + + if (handle == ACPI_ROOT_OBJECT) { + return AE_NULL_ENTRY; + } + + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + + /* Convert and validate the handle */ + + object = acpi_ns_convert_handle_to_entry (handle); + if (!object) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + + /* Get the parent entry */ + + *ret_handle = + acpi_ns_convert_entry_to_handle (acpi_ns_get_parent_entry (object)); + + /* Return exeption if parent is null */ + + if (!acpi_ns_get_parent_entry (object)) { + status = AE_NULL_ENTRY; + } + + +unlock_and_exit: + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + return AE_OK; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_walk_namespace + * + * PARAMETERS: Type - ACPI_OBJECT_TYPE to search for + * Start_object - Handle in namespace where search begins + * Max_depth - Depth to which search is to reach + * User_function - Called when an object of "Type" is found + * Context - Passed to user function + * + * RETURNS Return value from the User_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by Start_handle. + * The User_function is called whenever an object that matches + * the type parameter is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the User Function can be tailored + * to each task, whether it is a print function, a compare + * function, etc. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_walk_namespace ( + ACPI_OBJECT_TYPE type, + ACPI_HANDLE start_object, + u32 max_depth, + WALK_CALLBACK user_function, + void *context, + void **return_value) +{ + ACPI_STATUS status; + + + /* Parameter validation */ + + if ((type > ACPI_TYPE_MAX) || + (!max_depth) || + (!user_function)) + { + return (AE_BAD_PARAMETER); + } + + /* + * Lock the namespace around the walk. + * The namespace will be unlocked/locked around each call + * to the user function - since this function + * must be allowed to make Acpi calls itself. + */ + + acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE); + status = acpi_ns_walk_namespace ((OBJECT_TYPE_INTERNAL) type, + start_object, max_depth, + NS_WALK_UNLOCK, + user_function, context, + return_value); + + acpi_cm_release_mutex (ACPI_MTX_NAMESPACE); + + return (status); +} + + diff --git a/drivers/acpi/osd.c b/drivers/acpi/osd.c new file mode 100644 index 000000000..402df75d7 --- /dev/null +++ b/drivers/acpi/osd.c @@ -0,0 +1,1319 @@ +/* + * osd.c - Linux specific code + * + * Copyright (C) 2000 Andrew Henroid + * + * 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/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/sysctl.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/pm.h> +#include <linux/mm.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/uaccess.h> +#include "acpi.h" + +#define ACPI_MAX_THROTTLE 10 +#define ACPI_INVALID ~0UL +#define ACPI_INFINITE ~0UL + +static struct acpi_facp *acpi_facp = NULL; + +static spinlock_t acpi_event_lock = SPIN_LOCK_UNLOCKED; +static volatile u32 acpi_event_status = 0; +static volatile acpi_sstate_t acpi_event_state = ACPI_S0; +static DECLARE_WAIT_QUEUE_HEAD(acpi_event_wait); +static int acpi_irq_irq = 0; +static OSD_HANDLER acpi_irq_handler = NULL; +static void *acpi_irq_context = NULL; + +static unsigned long acpi_pblk = ACPI_INVALID; +static unsigned long acpi_c2_exit_latency = ACPI_INFINITE; +static unsigned long acpi_c3_exit_latency = ACPI_INFINITE; +static unsigned long acpi_c2_enter_latency = ACPI_INFINITE; +static unsigned long acpi_c3_enter_latency = ACPI_INFINITE; +static int acpi_c2_tested = 0; +static int acpi_c3_tested = 0; + +struct acpi_intrp_entry +{ + int priority; + OSD_EXECUTION_CALLBACK callback; + void *context; + struct list_head list; +}; + +struct acpi_enter_sx_ctx +{ + wait_queue_head_t wait; + int state; +}; + +static volatile int acpi_intrp_pid = -1; +static DECLARE_WAIT_QUEUE_HEAD(acpi_intrp_wait); +static LIST_HEAD(acpi_intrp_exec); +static spinlock_t acpi_intrp_exec_lock = SPIN_LOCK_UNLOCKED; + +#define ACPI_SLP_TYP(typa, typb) (((int)(typa) << 8) | (int)(typb)) +#define ACPI_SLP_TYPA(value) \ + ((((value) >> 8) << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK) +#define ACPI_SLP_TYPB(value) \ + ((((value) & 0xff) << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK) + +static volatile acpi_sstate_t acpi_sleep_state = ACPI_S0; +static unsigned long acpi_slptyp[ACPI_S5 + 1] = {ACPI_INVALID,}; + +static struct ctl_table_header *acpi_sysctl = NULL; + +static int acpi_do_ulong(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len); +static int acpi_do_sleep(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len); +static int acpi_do_event(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len); + +static struct ctl_table acpi_table[] = +{ + {ACPI_P_LVL2_LAT, "c2_exit_latency", + &acpi_c2_exit_latency, sizeof(acpi_c2_exit_latency), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_ENTER_LVL2_LAT, "c2_enter_latency", + &acpi_c2_enter_latency, sizeof(acpi_c2_enter_latency), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_P_LVL3_LAT, "c3_exit_latency", + &acpi_c3_exit_latency, sizeof(acpi_c3_exit_latency), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_ENTER_LVL3_LAT, "c3_enter_latency", + &acpi_c3_enter_latency, sizeof(acpi_c3_enter_latency), + 0644, NULL, &acpi_do_ulong}, + + {ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &acpi_do_sleep}, + + {ACPI_EVENT, "event", NULL, 0, 0400, NULL, &acpi_do_event}, + + {0} +}; + +static struct ctl_table acpi_dir_table[] = +{ + {CTL_ACPI, "acpi", NULL, 0, 0555, acpi_table}, + {0} +}; + +static u32 FASTCALL(acpi_read_pm1_control(struct acpi_facp *)); +static u32 FASTCALL(acpi_read_pm1_status(struct acpi_facp *)); +static void FASTCALL(acpi_write_pm1_control(struct acpi_facp *, u32)); +static void FASTCALL(acpi_write_pm1_status(struct acpi_facp *, u32)); + +/* + * Get the value of the PM1 control register (ACPI_SCI_EN, ...) + */ +static u32 +acpi_read_pm1_control(struct acpi_facp *facp) +{ + u32 value = 0; + if (facp->pm1a_cnt) + value = inw(facp->pm1a_cnt); + if (facp->pm1b_cnt) + value |= inw(facp->pm1b_cnt); + return value; +} + +/* + * Set the value of the PM1 control register (ACPI_BM_RLD, ...) + */ +static void +acpi_write_pm1_control(struct acpi_facp *facp, u32 value) +{ + if (facp->pm1a_cnt) + outw(value, facp->pm1a_cnt); + if (facp->pm1b_cnt) + outw(value, facp->pm1b_cnt); +} + +/* + * Get the value of the fixed event status register + */ +static u32 +acpi_read_pm1_status(struct acpi_facp *facp) +{ + u32 value = 0; + if (facp->pm1a_evt) + value = inw(facp->pm1a_evt); + if (facp->pm1b_evt) + value |= inw(facp->pm1b_evt); + return value; +} + +/* + * Set the value of the fixed event status register (clear events) + */ +static void +acpi_write_pm1_status(struct acpi_facp *facp, u32 value) +{ + if (facp->pm1a_evt) + outw(value, facp->pm1a_evt); + if (facp->pm1b_evt) + outw(value, facp->pm1b_evt); +} + +/* + * Examine/modify value + */ +static int +acpi_do_ulong(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len) +{ + char str[2 * sizeof(unsigned long) + 4], *strend; + unsigned long val; + int size; + + if (!write) { + if (file->f_pos) { + *len = 0; + return 0; + } + + val = *(unsigned long *) ctl->data; + size = sprintf(str, "0x%08lx\n", val); + if (*len >= size) { + copy_to_user(buffer, str, size); + *len = size; + } + else + *len = 0; + } + else { + size = sizeof(str) - 1; + if (size > *len) + size = *len; + copy_from_user(str, buffer, size); + str[size] = '\0'; + val = simple_strtoul(str, &strend, 0); + if (strend == str) + return -EINVAL; + *(unsigned long *) ctl->data = val; + } + + file->f_pos += *len; + return 0; +} + +/* + * Handle ACPI event + */ +u32 +acpi_event(void *context) +{ + unsigned long flags; + int event = (int) context; + int mask = 0; + + switch (event) { + case ACPI_EVENT_POWER_BUTTON: + mask = ACPI_PWRBTN; + break; + case ACPI_EVENT_SLEEP_BUTTON: + mask = ACPI_SLPBTN; + break; + } + + if (mask) { + // notify process waiting on /dev/acpi + spin_lock_irqsave(&acpi_event_lock, flags); + acpi_event_status |= mask; + spin_unlock_irqrestore(&acpi_event_lock, flags); + acpi_event_state = acpi_sleep_state; + wake_up_interruptible(&acpi_event_wait); + } + + return AE_OK; +} + +/* + * Wait for next event + */ +int +acpi_do_event(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len) +{ + u32 event_status = 0; + acpi_sstate_t event_state = 0; + char str[27]; + int size; + + if (write) + return -EPERM; + if (*len < sizeof(str)) { + *len = 0; + return 0; + } + + for (;;) { + unsigned long flags; + + // we need an atomic exchange here + spin_lock_irqsave(&acpi_event_lock, flags); + event_status = acpi_event_status; + acpi_event_status = 0; + spin_unlock_irqrestore(&acpi_event_lock, flags); + event_state = acpi_event_state; + + if (event_status) + break; + + // wait for an event to arrive + interruptible_sleep_on(&acpi_event_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + size = sprintf(str, + "0x%08x 0x%08x 0x%01x\n", + event_status, + 0, + event_state); + copy_to_user(buffer, str, size); + *len = size; + file->f_pos += size; + + return 0; +} + +/* + * Enter system sleep state + */ +static void +acpi_enter_sx(void *context) +{ + struct acpi_enter_sx_ctx *ctx = (struct acpi_enter_sx_ctx*) context; + struct acpi_facp *facp = acpi_facp; + ACPI_OBJECT_LIST arg_list; + ACPI_OBJECT arg; + u16 value; + + /* + * _PSW methods could be run here to enable wake-on keyboard, LAN, etc. + */ + + // run the _PTS method + memset(&arg_list, 0, sizeof(arg_list)); + arg_list.count = 1; + arg_list.pointer = &arg; + + memset(&arg, 0, sizeof(arg)); + arg.type = ACPI_TYPE_NUMBER; + arg.number.value = ctx->state; + + acpi_evaluate_object(NULL, "\\_PTS", &arg_list, NULL); + + // clear wake status + acpi_write_pm1_status(facp, ACPI_WAK); + + acpi_sleep_state = ctx->state; + + // set ACPI_SLP_TYPA/b and ACPI_SLP_EN + __cli(); + if (facp->pm1a_cnt) { + value = inw(facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK; + value |= (ACPI_SLP_TYPA(acpi_slptyp[ctx->state]) + | ACPI_SLP_EN); + outw(value, facp->pm1a_cnt); + } + if (facp->pm1b_cnt) { + value = inw(facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK; + value |= (ACPI_SLP_TYPB(acpi_slptyp[ctx->state]) + | ACPI_SLP_EN); + outw(value, facp->pm1b_cnt); + } + __sti(); + + if (ctx->state != ACPI_S1) { + printk(KERN_ERR "ACPI: S%d failed\n", ctx->state); + goto out; + } + + // wait until S1 is entered + while (!(acpi_read_pm1_status(facp) & ACPI_WAK)) + safe_halt(); + + // run the _WAK method + memset(&arg_list, 0, sizeof(arg_list)); + arg_list.count = 1; + arg_list.pointer = &arg; + + memset(&arg, 0, sizeof(arg)); + arg.type = ACPI_TYPE_NUMBER; + arg.number.value = ctx->state; + + acpi_evaluate_object(NULL, "\\_WAK", &arg_list, NULL); + + out: + acpi_sleep_state = ACPI_S0; + + if (waitqueue_active(&ctx->wait)) + wake_up_interruptible(&ctx->wait); +} + +/* + * Enter system sleep state and wait for completion + */ +static int +acpi_enter_sx_and_wait(acpi_sstate_t state) +{ + struct acpi_enter_sx_ctx ctx; + + if (!acpi_facp + || acpi_facp->hdr.signature != ACPI_FACP_SIG + || acpi_slptyp[state] == ACPI_INVALID) + return -EINVAL; + + init_waitqueue_head(&ctx.wait); + ctx.state = state; + + if (acpi_os_queue_for_execution(0, acpi_enter_sx, &ctx)) + return -1; + + interruptible_sleep_on(&ctx.wait); + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; +} + +/* + * Enter system sleep state + */ +static int +acpi_do_sleep(ctl_table * ctl, + int write, + struct file *file, + void *buffer, + size_t * len) +{ + if (!write) { + if (file->f_pos) { + *len = 0; + return 0; + } + } + else { +#ifdef CONFIG_ACPI_S1_SLEEP + int status = acpi_enter_sx_and_wait(ACPI_S1); + if (status) + return status; +#endif + } + file->f_pos += *len; + return 0; +} + +/* + * Clear busmaster activity flag + */ +static inline void +acpi_clear_bm_activity(struct acpi_facp *facp) +{ + acpi_write_pm1_status(facp, ACPI_BM); +} + +/* + * Returns 1 if there has been busmaster activity + */ +static inline int +acpi_bm_activity(struct acpi_facp *facp) +{ + return acpi_read_pm1_status(facp) & ACPI_BM; +} + +/* + * Set system to sleep through busmaster requests + */ +static void +acpi_sleep_on_busmaster(struct acpi_facp *facp) +{ + u32 pm1_cntr = acpi_read_pm1_control(facp); + if (pm1_cntr & ACPI_BM_RLD) { + pm1_cntr &= ~ACPI_BM_RLD; + acpi_write_pm1_control(facp, pm1_cntr); + } +} + +/* + * Set system to wake on busmaster requests + */ +static void +acpi_wake_on_busmaster(struct acpi_facp *facp) +{ + u32 pm1_cntr = acpi_read_pm1_control(facp); + if (!(pm1_cntr & ACPI_BM_RLD)) { + pm1_cntr |= ACPI_BM_RLD; + acpi_write_pm1_control(facp, pm1_cntr); + } + acpi_clear_bm_activity(facp); +} + +/* The ACPI timer is just the low 24 bits */ +#define TIME_BEGIN(tmr) inl(tmr) +#define TIME_END(tmr, begin) ((inl(tmr) - (begin)) & 0x00ffffff) + +/* + * Idle loop (uniprocessor only) + */ +void +acpi_idle(void) +{ + static int sleep_level = 1; + struct acpi_facp *facp = acpi_facp; + + if (!facp + || facp->hdr.signature != ACPI_FACP_SIG + || !facp->pm_tmr + || !acpi_pblk) + goto not_initialized; + + /* + * start from the previous sleep level.. + */ + if (sleep_level == 1) + goto sleep1; + if (sleep_level == 2) + goto sleep2; + sleep3: + sleep_level = 3; + if (!acpi_c3_tested) { + printk(KERN_DEBUG "ACPI C3 works\n"); + acpi_c3_tested = 1; + } + acpi_wake_on_busmaster(facp); + if (facp->pm2_cnt) + goto sleep3_with_arbiter; + + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + if (acpi_bm_activity(facp)) + goto sleep2; + + time = TIME_BEGIN(pm_tmr); + inb(acpi_pblk + ACPI_P_LVL3); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + + __sti(); + if (time < acpi_c3_exit_latency) + goto sleep2; + } + + sleep3_with_arbiter: + for (;;) { + unsigned long time; + u8 arbiter; + unsigned int pm2_cntr = facp->pm2_cnt; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + if (acpi_bm_activity(facp)) + goto sleep2; + + time = TIME_BEGIN(pm_tmr); + arbiter = inb(pm2_cntr) & ~ACPI_ARB_DIS; + /* Disable arbiter, park on CPU */ + outb(arbiter | ACPI_ARB_DIS, pm2_cntr); + inb(acpi_pblk + ACPI_P_LVL3); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + /* Enable arbiter again.. */ + outb(arbiter, pm2_cntr); + + __sti(); + if (time < acpi_c3_exit_latency) + goto sleep2; + } + + sleep2: + sleep_level = 2; + if (!acpi_c2_tested) { + printk(KERN_DEBUG "ACPI C2 works\n"); + acpi_c2_tested = 1; + } + acpi_wake_on_busmaster(facp); /* Required to track BM activity.. */ + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + + time = TIME_BEGIN(pm_tmr); + inb(acpi_pblk + ACPI_P_LVL2); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + + __sti(); + if (time < acpi_c2_exit_latency) + goto sleep1; + if (acpi_bm_activity(facp)) { + acpi_clear_bm_activity(facp); + continue; + } + if (time > acpi_c3_enter_latency) + goto sleep3; + } + + sleep1: + sleep_level = 1; + acpi_sleep_on_busmaster(facp); + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + time = TIME_BEGIN(pm_tmr); + safe_halt(); + time = TIME_END(pm_tmr, time); + if (time > acpi_c2_enter_latency) + goto sleep2; + } + + not_initialized: + for (;;) { + __cli(); + if (current->need_resched) + goto out; + safe_halt(); + } + + out: + __sti(); +} + +/* + * Enter soft-off (S5) + */ +static void +acpi_power_off(void) +{ + struct acpi_enter_sx_ctx ctx; + + if (!acpi_facp + || acpi_facp->hdr.signature != ACPI_FACP_SIG + || acpi_slptyp[ACPI_S5] == ACPI_INVALID) + return; + + init_waitqueue_head(&ctx.wait); + ctx.state = ACPI_S5; + acpi_enter_sx(&ctx); +} + +/* + * Get processor information + */ +static ACPI_STATUS +acpi_get_cpu_info(ACPI_HANDLE handle, u32 level, void *ctx, void **value) +{ + ACPI_OBJECT obj; + ACPI_CX_STATE lat[4]; + ACPI_CPU_THROTTLING_STATE throttle[ACPI_MAX_THROTTLE]; + ACPI_BUFFER buf; + int i, count; + + buf.length = sizeof(obj); + buf.pointer = &obj; + if (!ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buf))) + return AE_OK; + + printk(KERN_INFO "ACPI: PBLK %d @ 0x%04x:%d\n", + obj.processor.proc_id, + obj.processor.pblk_address, + obj.processor.pblk_length); + if (acpi_pblk != ACPI_INVALID + || !obj.processor.pblk_address + || obj.processor.pblk_length != 6) + return AE_OK; + + acpi_pblk = obj.processor.pblk_address; + + buf.length = sizeof(lat); + buf.pointer = lat; + if (!ACPI_SUCCESS(acpi_get_processor_cx_info(handle, &buf))) + return AE_OK; + + if (lat[2].latency < MAX_CX_STATE_LATENCY) { + printk(KERN_INFO "ACPI: C2 supported\n"); + acpi_c2_exit_latency = lat[2].latency; + } + if (lat[3].latency < MAX_CX_STATE_LATENCY) { + printk(KERN_INFO "ACPI: C3 supported\n"); + acpi_c3_exit_latency = lat[3].latency; + } + + memset(throttle, 0, sizeof(throttle)); + buf.length = sizeof(throttle); + buf.pointer = throttle; + + if (!ACPI_SUCCESS(acpi_get_processor_throttling_info(handle, &buf))) + return AE_OK; + + for (i = 0, count = 0; i < ACPI_MAX_THROTTLE; i++) { + if (throttle[i].percent_of_clock) + count++; + } + count--; + if (count > 0) + printk(KERN_INFO "ACPI: %d throttling states\n", count); + + return AE_OK; +} + +/* + * Fetch the FACP information + */ +static int +acpi_fetch_facp(void) +{ + ACPI_BUFFER buffer; + + buffer.pointer = acpi_facp; + buffer.length = sizeof(*acpi_facp); + if (!ACPI_SUCCESS(acpi_get_table(ACPI_TABLE_FACP, 1, &buffer))) { + printk(KERN_ERR "ACPI: no FACP\n"); + kfree(acpi_facp); + return -ENODEV; + } + + if (acpi_facp->p_lvl2_lat + && acpi_facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) { + acpi_c2_exit_latency + = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl2_lat); + acpi_c2_enter_latency + = ACPI_uS_TO_TMR_TICKS(ACPI_TMR_HZ / 1000); + } + if (acpi_facp->p_lvl3_lat + && acpi_facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) { + acpi_c3_exit_latency + = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat); + acpi_c3_enter_latency + = ACPI_uS_TO_TMR_TICKS(acpi_facp->p_lvl3_lat * 5); + } + + return 0; +} + +/* + * Execute callbacks (interpret methods) + */ +static void +acpi_intrp_run(void) +{ + for (;;) { + struct acpi_intrp_entry *entry; + unsigned long flags; + + interruptible_sleep_on(&acpi_intrp_wait); + if (signal_pending(current)) + return; + + for (;;) { + entry = NULL; + + spin_lock_irqsave(&acpi_intrp_exec_lock, flags); + if (!list_empty(&acpi_intrp_exec)) { + entry = list_entry(acpi_intrp_exec.next, + struct acpi_intrp_entry, + list); + list_del(&entry->list); + } + spin_unlock_irqrestore(&acpi_intrp_exec_lock, flags); + + if (!entry) + break; + + (*entry->callback)(entry->context); + + kfree(entry); + } + } +} + +/* + * Initialize and run interpreter within a kernel thread + */ +static int +acpi_intrp_thread(void *context) +{ + ACPI_STATUS status; + u8 sx, typa, typb; + + /* + * initialize interpreter + */ + exit_files(current); + daemonize(); + strcpy(current->comm, "acpi"); + + if (!ACPI_SUCCESS(acpi_initialize(NULL))) { + printk(KERN_ERR "ACPI: initialize failed\n"); + return -ENODEV; + } + + if (!ACPI_SUCCESS(acpi_load_firmware_tables())) { + printk(KERN_ERR "ACPI: table load failed\n"); + acpi_terminate(); + return -ENODEV; + } + + if (acpi_fetch_facp()) { + acpi_terminate(); + return -ENODEV; + } + + status = acpi_load_namespace(); + if (!ACPI_SUCCESS(status) && status != AE_CTRL_PENDING) { + printk(KERN_ERR "ACPI: namespace load failed\n"); + acpi_terminate(); + return -ENODEV; + } + + printk(KERN_INFO "ACPI: ACPI support found\n"); + + acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + ACPI_ROOT_OBJECT, + ACPI_INT32_MAX, + acpi_get_cpu_info, + NULL, + NULL); + + for (sx = ACPI_S0; sx <= ACPI_S5; sx++) { + int ca_sx = (sx <= ACPI_S4) ? sx : (sx + 1); + if (ACPI_SUCCESS( + acpi_hw_obtain_sleep_type_register_data(ca_sx, + &typa, + &typb))) + acpi_slptyp[sx] = ACPI_SLP_TYP(typa, typb); + else + acpi_slptyp[sx] = ACPI_INVALID; + } + if (acpi_slptyp[ACPI_S1] != ACPI_INVALID) + printk(KERN_INFO "ACPI: S1 supported\n"); + if (acpi_slptyp[ACPI_S5] != ACPI_INVALID) + printk(KERN_INFO "ACPI: S5 supported\n"); + + if (!ACPI_SUCCESS(acpi_enable())) { + printk(KERN_ERR "ACPI: enable failed\n"); + acpi_terminate(); + return -ENODEV; + } + + if (!ACPI_SUCCESS(acpi_install_fixed_event_handler( + ACPI_EVENT_POWER_BUTTON, + acpi_event, + (void *) ACPI_EVENT_POWER_BUTTON))) { + printk(KERN_ERR "ACPI: power button enable failed\n"); + } + if (!ACPI_SUCCESS(acpi_install_fixed_event_handler( + ACPI_EVENT_SLEEP_BUTTON, + acpi_event, + (void *) ACPI_EVENT_SLEEP_BUTTON))) { + printk(KERN_ERR "ACPI: sleep button enable failed\n"); + } + +#ifdef CONFIG_SMP + if (smp_num_cpus == 1) + pm_idle = acpi_idle; +#else + pm_idle = acpi_idle; +#endif + pm_power_off = acpi_power_off; + + acpi_sysctl = register_sysctl_table(acpi_dir_table, 1); + + /* + * run interpreter + */ + acpi_intrp_run(); + + /* + * terminate interpreter + */ + unregister_sysctl_table(acpi_sysctl); + acpi_terminate(); + + acpi_intrp_pid = -1; + + return 0; +} + +/* + * Start the interpreter + */ +int __init +acpi_init(void) +{ + acpi_facp = kmalloc(sizeof(*acpi_facp), GFP_KERNEL); + if (!acpi_facp) + return -ENOMEM; + memset(acpi_facp, 0, sizeof(*acpi_facp)); + + acpi_intrp_pid = kernel_thread(acpi_intrp_thread, + NULL, + (CLONE_FS | CLONE_FILES + | CLONE_SIGHAND | SIGCHLD)); + return ((acpi_intrp_pid >= 0) ? 0:-ENODEV); +} + +/* + * Terminate the interpreter + */ +void __exit +acpi_exit(void) +{ + int count; + + if (!kill_proc(acpi_intrp_pid, SIGTERM, 1)) { + // wait until interpreter thread terminates (at most 5 seconds) + count = 5 * HZ; + while (acpi_intrp_pid >= 0 && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + } + + // clean up after the interpreter + + if (acpi_irq_handler) { + acpi_os_remove_interrupt_handler(acpi_irq_irq, + acpi_irq_handler); + } + + if (pm_power_off == acpi_power_off) + pm_power_off = NULL; + if (pm_idle == acpi_idle) + pm_idle = NULL; + + kfree(acpi_facp); +} + +module_init(acpi_init); +module_exit(acpi_exit); + +/* + * OS-dependent functions + * + */ + +char * +strupr(char *str) +{ + char *s = str; + while (*s) { + *s = TOUPPER(*s); + s++; + } + return str; +} + +ACPI_STATUS +acpi_os_initialize(void) +{ + return AE_OK; +} + +ACPI_STATUS +acpi_os_terminate(void) +{ + return AE_OK; +} + +s32 +acpi_os_printf(const 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 char *fmt, va_list args) +{ + static char buffer[512]; + int size = vsprintf(buffer, fmt, args); + printk(KERN_DEBUG "%s", buffer); + 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_map_memory(void *phys, u32 size, void **virt) +{ + if ((unsigned long) phys < virt_to_phys(high_memory)) { + *virt = phys_to_virt((unsigned long) phys); + return AE_OK; + } + + *virt = ioremap((unsigned long) phys, size); + if (!*virt) + return AE_ERROR; + + return AE_OK; +} + +void +acpi_os_unmap_memory(void *virt, u32 size) +{ + if (virt >= high_memory) + iounmap(virt); +} + +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) +{ + acpi_irq_irq = irq; + acpi_irq_handler = handler; + acpi_irq_context = context; + if (request_irq(irq, + acpi_irq, + SA_INTERRUPT | 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) { + 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_sleep_usec(u32 us) +{ + udelay(us); +} + +u8 +acpi_os_in8(ACPI_IO_ADDRESS port) +{ + return inb(port); +} + +u16 +acpi_os_in16(ACPI_IO_ADDRESS port) +{ + return inw(port); +} + +u32 +acpi_os_in32(ACPI_IO_ADDRESS port) +{ + return inl(port); +} + +void +acpi_os_out8(ACPI_IO_ADDRESS port, u8 val) +{ + outb(val, port); +} + +void +acpi_os_out16(ACPI_IO_ADDRESS port, u16 val) +{ + outw(val, port); +} + +void +acpi_os_out32(ACPI_IO_ADDRESS port, u32 val) +{ + outl(val, port); +} + +ACPI_STATUS +acpi_os_read_pci_cfg_byte( + u32 bus, + u32 func, + u32 addr, + u8 * val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!val || !dev || pci_read_config_byte(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +ACPI_STATUS +acpi_os_read_pci_cfg_word( + u32 bus, + u32 func, + u32 addr, + u16 * val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!val || !dev || pci_read_config_word(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +ACPI_STATUS +acpi_os_read_pci_cfg_dword( + u32 bus, + u32 func, + u32 addr, + u32 * val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!val || !dev || pci_read_config_dword(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +ACPI_STATUS +acpi_os_write_pci_cfg_byte( + u32 bus, + u32 func, + u32 addr, + u8 val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!dev || pci_write_config_byte(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +ACPI_STATUS +acpi_os_write_pci_cfg_word( + u32 bus, + u32 func, + u32 addr, + u16 val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!dev || pci_write_config_word(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +ACPI_STATUS +acpi_os_write_pci_cfg_dword( + u32 bus, + u32 func, + u32 addr, + u32 val) +{ + int devfn = PCI_DEVFN((func >> 16) & 0xffff, func & 0xffff); + struct pci_dev *dev = pci_find_slot(bus & 0xffff, devfn); + if (!dev || pci_write_config_dword(dev, addr, val)) + return AE_ERROR; + return AE_OK; +} + +/* + * Queue for interpreter thread + */ + +ACPI_STATUS +acpi_os_queue_for_execution( + u32 priority, + OSD_EXECUTION_CALLBACK callback, + void *context) +{ + struct acpi_intrp_entry *entry; + unsigned long flags; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return AE_ERROR; + + memset(entry, 0, sizeof(entry)); + entry->priority = priority; + entry->callback = callback; + entry->context = context; + INIT_LIST_HEAD(&entry->list); + + if (!waitqueue_active(&acpi_intrp_wait)) { + kfree(entry); + return AE_ERROR; + } + + spin_lock_irqsave(&acpi_intrp_exec_lock, flags); + list_add(&entry->list, &acpi_intrp_exec); + wake_up(&acpi_intrp_wait); + spin_unlock_irqrestore(&acpi_intrp_exec_lock, flags); + + return AE_OK; +} + +/* + * Semaphores are unused, interpreter access is single threaded + */ + +ACPI_STATUS +acpi_os_create_semaphore(u32 max_units, u32 init, ACPI_HANDLE * handle) +{ + *handle = (ACPI_HANDLE) 0; + return AE_OK; +} + +ACPI_STATUS +acpi_os_delete_semaphore(ACPI_HANDLE handle) +{ + return AE_OK; +} + +ACPI_STATUS +acpi_os_wait_semaphore(ACPI_HANDLE handle, u32 units, u32 timeout) +{ + return AE_OK; +} + +ACPI_STATUS +acpi_os_signal_semaphore(ACPI_HANDLE handle, u32 units) +{ + return AE_OK; +} + +ACPI_STATUS +acpi_os_breakpoint(char *msg) +{ + acpi_os_printf("breakpoint: %s", msg); + return AE_OK; +} + +void +acpi_os_dbg_trap(char *msg) +{ + acpi_os_printf("trap: %s", msg); +} + +void +acpi_os_dbg_assert(void *failure, void *file, u32 line, char *msg) +{ + acpi_os_printf("assert: %s", msg); +} + +u32 +acpi_os_get_line(char *buffer) +{ + 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; +} diff --git a/drivers/acpi/parser/psargs.c b/drivers/acpi/parser/psargs.c new file mode 100644 index 000000000..b9a5b5b5d --- /dev/null +++ b/drivers/acpi/parser/psargs.c @@ -0,0 +1,735 @@ +/****************************************************************************** + * + * Module Name: psargs - Parse AML opcode arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" +#include "namesp.h" + +#define _COMPONENT PARSER + MODULE_NAME ("psargs"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_package_length + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Decoded package length. On completion, the AML pointer points + * past the length byte or bytes. + * + * DESCRIPTION: Decode and return a package length field + * + ******************************************************************************/ + +u32 +acpi_ps_get_next_package_length ( + ACPI_PARSE_STATE *parser_state) +{ + s32 encoded_length; + s32 length = 0; + + + encoded_length = (s32) GET8 (parser_state->aml); + parser_state->aml++; + + + switch (encoded_length >> 6) /* bits 6-7 contain encoding scheme */ + { + case 0: /* 1-byte encoding (bits 0-5) */ + + length = (encoded_length & 0x3f); + break; + + + case 1: /* 2-byte encoding (next byte + bits 0-3) */ + + length = (GET8 (parser_state->aml) << 4) | (encoded_length & 0xf); + parser_state->aml++; + break; + + + case 2: /* 3-byte encoding (next 2 bytes + bits 0-3) */ + + length = ( (GET8 (parser_state->aml + 1) << 12) + | (GET8 (parser_state->aml) << 4) + | (encoded_length & 0xf)); + parser_state->aml += 2; + break; + + + case 3: /* 4-byte encoding (next 3 bytes + bits 0-3) */ + + length = ( (GET8 (parser_state->aml + 2) << 20) + | (GET8 (parser_state->aml + 1) << 12) + | (GET8 (parser_state->aml) << 4) + | (encoded_length & 0xf)); + parser_state->aml += 3; + break; + } + + return (length); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_package_end + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Pointer to end-of-package +1 + * + * DESCRIPTION: Get next package length and return a pointer past the end of + * the package. Consumes the package length field + * + ******************************************************************************/ + +u8 * +acpi_ps_get_next_package_end ( + ACPI_PARSE_STATE *parser_state) +{ + u8 *start = parser_state->aml; + NATIVE_UINT length; + + + length = (NATIVE_UINT) acpi_ps_get_next_package_length (parser_state); + + return (start + length); /* end of package */ +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_namestring + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Pointer to the start of the name string (pointer points into + * the AML. + * + * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name + * prefix characters. Set parser state to point past the string. + * (Name is consumed from the AML.) + * + ******************************************************************************/ + +char * +acpi_ps_get_next_namestring ( + ACPI_PARSE_STATE *parser_state) +{ + char *start = (char *) parser_state->aml; + char *end = (char *) parser_state->aml; + s32 length; + + + /* Handle multiple prefix characters */ + + while (acpi_ps_is_prefix_char (GET8 (end))) { + /* include prefix '\\' or '^' */ + + end++; + } + + /* Decode the path */ + + switch (GET8 (end)) + { + case 0: + + /* Null_name */ + + if (end == start) { + start = NULL; + } + end++; + break; + + + case AML_DUAL_NAME_PREFIX: + + /* two name segments */ + + end += 9; + break; + + + case AML_MULTI_NAME_PREFIX_OP: + + /* multiple name segments */ + + length = (s32) GET8 (end + 1) * 4; + end += 2 + length; + break; + + + default: + + /* single name segment */ + /* assert (Acpi_ps_is_lead (GET8 (End))); */ + + end += 4; + break; + } + + parser_state->aml = (u8*) end; + + return (start); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_namepath + * + * PARAMETERS: Parser_state - Current parser state object + * Arg - Where the namepath will be stored + * Arg_count - If the namepath points to a control method + * the method's argument is returned here. + * Method_call - Whether the namepath can be the start + * of a method call + * + * RETURN: None + * + * DESCRIPTION: Get next name (if method call, push appropriate # args). Names + * are looked up in either the parsed or internal namespace to + * determine if the name represents a control method. If a method + * is found, the number of arguments to the method is returned. + * This information is critical for parsing to continue correctly. + * + ******************************************************************************/ + + +#ifdef PARSER_ONLY + +void +acpi_ps_get_next_namepath ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *arg, + u32 *arg_count, + u8 method_call) +{ + char *path; + ACPI_GENERIC_OP *name; + ACPI_GENERIC_OP *op; + ACPI_GENERIC_OP *count; + + + path = acpi_ps_get_next_namestring (parser_state); + if (!path || !method_call) { + /* Null name case, create a null namepath object */ + + acpi_ps_init_op (arg, AML_NAMEPATH_OP); + arg->value.name = path; + return; + } + + + if (acpi_gbl_parsed_namespace_root) { + /* + * Lookup the name in the parsed namespace + */ + + op = NULL; + if (method_call) { + op = acpi_ps_find (acpi_ps_get_parent_scope (parser_state), + path, AML_METHOD_OP, 0); + } + + if (op) { + if (op->opcode == AML_METHOD_OP) { + /* + * The name refers to a control method, so this namepath is a + * method invocation. We need to 1) Get the number of arguments + * associated with this method, and 2) Change the NAMEPATH + * object into a METHODCALL object. + */ + + count = acpi_ps_get_arg (op, 0); + if (count && count->opcode == AML_BYTE_OP) { + name = acpi_ps_alloc_op (AML_NAMEPATH_OP); + if (name) { + /* Change arg into a METHOD CALL and attach the name */ + + acpi_ps_init_op (arg, AML_METHODCALL_OP); + + name->value.name = path; + + /* Point METHODCALL/NAME to the METHOD NTE */ + + name->acpi_named_object = op; + acpi_ps_append_arg (arg, name); + + *arg_count = count->value.integer & + METHOD_FLAGS_ARG_COUNT; + } + } + + return; + } + + /* + * Else this is normal named object reference. + * Just init the NAMEPATH object with the pathname. + * (See code below) + */ + } + } + + + /* + * Either we didn't find the object in the namespace, or the object is + * something other than a control method. Just initialize the Op with the + * pathname + */ + + acpi_ps_init_op (arg, AML_NAMEPATH_OP); + arg->value.name = path; + + + return; +} + + +#else + + +void +acpi_ps_get_next_namepath ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *arg, + u32 *arg_count, + u8 method_call) +{ + char *path; + ACPI_GENERIC_OP *name; + ACPI_STATUS status; + ACPI_NAMED_OBJECT *method = NULL; + ACPI_NAMED_OBJECT *entry; + ACPI_GENERIC_STATE scope_info; + + + path = acpi_ps_get_next_namestring (parser_state); + if (!path || !method_call) { + /* Null name case, create a null namepath object */ + + acpi_ps_init_op (arg, AML_NAMEPATH_OP); + arg->value.name = path; + return; + } + + + if (method_call) { + /* + * Lookup the name in the internal namespace + */ + scope_info.scope.name_table = NULL; + entry = parser_state->start_op->acpi_named_object; + if (entry) { + scope_info.scope.name_table = entry->child_table; + } + + /* + * Lookup object. We don't want to add anything new to the namespace + * here, however. So we use MODE_EXECUTE. Allow searching of the + * parent tree, but don't open a new scope -- we just want to lookup the + * object (MUST BE mode EXECUTE to perform upsearch) + */ + + status = acpi_ns_lookup (&scope_info, path, ACPI_TYPE_ANY, IMODE_EXECUTE, + NS_SEARCH_PARENT | NS_DONT_OPEN_SCOPE, NULL, + &entry); + if (ACPI_SUCCESS (status)) { + if (entry->type == ACPI_TYPE_METHOD) { + method = entry; + name = acpi_ps_alloc_op (AML_NAMEPATH_OP); + if (name) { + /* Change arg into a METHOD CALL and attach name to it */ + + acpi_ps_init_op (arg, AML_METHODCALL_OP); + + name->value.name = path; + + /* Point METHODCALL/NAME to the METHOD NTE */ + + name->acpi_named_object = method; + acpi_ps_append_arg (arg, name); + + *arg_count = ((ACPI_OBJECT_INTERNAL *) method->object)->method.param_count; + } + + return; + } + + /* + * Else this is normal named object reference. + * Just init the NAMEPATH object with the pathname. + * (See code below) + */ + } + } + + /* + * Either we didn't find the object in the namespace, or the object is + * something other than a control method. Just initialize the Op with the + * pathname + */ + + acpi_ps_init_op (arg, AML_NAMEPATH_OP); + arg->value.name = path; + + + return; +} + +#endif + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_simple_arg + * + * PARAMETERS: Parser_state - Current parser state object + * Arg_type - The argument type (AML_*_ARG) + * Arg - Where the argument is returned + * + * RETURN: None + * + * DESCRIPTION: Get the next simple argument (constant, string, or namestring) + * + ******************************************************************************/ + +void +acpi_ps_get_next_simple_arg ( + ACPI_PARSE_STATE *parser_state, + s32 arg_type, + ACPI_GENERIC_OP *arg) +{ + + + switch (arg_type) + { + + case ARGP_BYTEDATA: + + acpi_ps_init_op (arg, AML_BYTE_OP); + arg->value.integer = (u32) GET8 (parser_state->aml); + parser_state->aml++; + break; + + + case ARGP_WORDDATA: + + acpi_ps_init_op (arg, AML_WORD_OP); + + /* Get 2 bytes from the AML stream */ + + MOVE_UNALIGNED16_TO_32 (&arg->value.integer, parser_state->aml); + parser_state->aml += 2; + break; + + + case ARGP_DWORDDATA: + + acpi_ps_init_op (arg, AML_DWORD_OP); + + /* Get 4 bytes from the AML stream */ + + MOVE_UNALIGNED32_TO_32 (&arg->value.integer, parser_state->aml); + parser_state->aml += 4; + break; + + + case ARGP_CHARLIST: + + acpi_ps_init_op (arg, AML_STRING_OP); + arg->value.string = (char*) parser_state->aml; + + while (GET8 (parser_state->aml) != '\0') { + parser_state->aml++; + } + parser_state->aml++; + break; + + + case ARGP_NAME: + case ARGP_NAMESTRING: + + acpi_ps_init_op (arg, AML_NAMEPATH_OP); + arg->value.name = acpi_ps_get_next_namestring (parser_state); + break; + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_field + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: A newly allocated FIELD op + * + * DESCRIPTION: Get next field (Named_field, Reserved_field, or Access_field) + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_next_field ( + ACPI_PARSE_STATE *parser_state) +{ + ACPI_PTRDIFF aml_offset = parser_state->aml - + parser_state->aml_start; + ACPI_GENERIC_OP *field; + u16 opcode; + u32 name; + + + /* determine field type */ + + switch (GET8 (parser_state->aml)) + { + + default: + + opcode = AML_NAMEDFIELD_OP; + break; + + + case 0x00: + + opcode = AML_RESERVEDFIELD_OP; + parser_state->aml++; + break; + + + case 0x01: + + opcode = AML_ACCESSFIELD_OP; + parser_state->aml++; + break; + } + + + /* Allocate a new field op */ + + field = acpi_ps_alloc_op (opcode); + if (field) { + field->aml_offset = aml_offset; + + /* Decode the field type */ + + switch (opcode) + { + case AML_NAMEDFIELD_OP: + + /* Get the 4-character name */ + + MOVE_UNALIGNED32_TO_32 (&name, parser_state->aml); + acpi_ps_set_name (field, name); + parser_state->aml += 4; + + /* Get the length which is encoded as a package length */ + + field->value.size = acpi_ps_get_next_package_length (parser_state); + break; + + + case AML_RESERVEDFIELD_OP: + + /* Get the length which is encoded as a package length */ + + field->value.size = acpi_ps_get_next_package_length (parser_state); + break; + + + case AML_ACCESSFIELD_OP: + + /* Get Access_type and Access_atrib and merge into the field Op */ + + field->value.integer = ((GET8 (parser_state->aml) << 8) | + GET8 (parser_state->aml)); + parser_state->aml += 2; + break; + } + } + + return (field); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_arg + * + * PARAMETERS: Parser_state - Current parser state object + * Arg_type - The argument type (AML_*_ARG) + * Arg_count - If the argument points to a control method + * the method's argument is returned here. + * + * RETURN: An op object containing the next argument. + * + * DESCRIPTION: Get next argument (including complex list arguments that require + * pushing the parser stack) + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_next_arg ( + ACPI_PARSE_STATE *parser_state, + s32 arg_type, + u32 *arg_count) +{ + ACPI_GENERIC_OP *arg = NULL; + ACPI_GENERIC_OP *prev = NULL; + ACPI_GENERIC_OP *field; + s32 subop; + + + switch (arg_type) + { + case ARGP_BYTEDATA: + case ARGP_WORDDATA: + case ARGP_DWORDDATA: + case ARGP_CHARLIST: + case ARGP_NAME: + case ARGP_NAMESTRING: + + /* constants, strings, and namestrings are all the same size */ + + arg = acpi_ps_alloc_op (AML_BYTE_OP); + if (arg) { + acpi_ps_get_next_simple_arg (parser_state, arg_type, arg); + } + break; + + + case ARGP_PKGLENGTH: + + /* package length, nothing returned */ + + parser_state->pkg_end = acpi_ps_get_next_package_end (parser_state); + break; + + + case ARGP_FIELDLIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* non-empty list */ + + while (parser_state->aml < parser_state->pkg_end) { + field = acpi_ps_get_next_field (parser_state); + if (!field) { + break; + } + + if (prev) { + prev->next = field; + } + + else { + arg = field; + } + + prev = field; + } + + /* skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + + case ARGP_BYTELIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* non-empty list */ + + arg = acpi_ps_alloc_op (AML_BYTELIST_OP); + if (arg) { + /* fill in bytelist data */ + + arg->value.size = (parser_state->pkg_end - parser_state->aml); + acpi_ps_to_bytelist_op (arg)->data = parser_state->aml; + } + + /* skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + + case ARGP_TARGET: + case ARGP_SUPERNAME: + { + subop = acpi_ps_peek_opcode (parser_state); + if (subop == 0 || + acpi_ps_is_leading_char (subop) || + acpi_ps_is_prefix_char (subop)) + { + /* Null_name or Name_string */ + + arg = acpi_ps_alloc_op (AML_NAMEPATH_OP); + if (arg) { + acpi_ps_get_next_namepath (parser_state, arg, arg_count, 0); + } + } + + else { + /* single complex argument, nothing returned */ + + *arg_count = 1; + } + } + break; + + + case ARGP_DATAOBJ: + case ARGP_TERMARG: + + /* single complex argument, nothing returned */ + + *arg_count = 1; + break; + + + case ARGP_DATAOBJLIST: + case ARGP_TERMLIST: + case ARGP_OBJLIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* non-empty list of variable arguments, nothing returned */ + + *arg_count = ACPI_VAR_ARGS; + } + break; + } + + return (arg); +} diff --git a/drivers/acpi/parser/psopcode.c b/drivers/acpi/parser/psopcode.c new file mode 100644 index 000000000..682084e90 --- /dev/null +++ b/drivers/acpi/parser/psopcode.c @@ -0,0 +1,554 @@ +/****************************************************************************** + * + * Module Name: psopcode - Parser opcode information table + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" + + +#define _COMPONENT PARSER + MODULE_NAME ("psopcode"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_opcode_info + * + * PARAMETERS: Opcode - The AML opcode + * + * RETURN: A pointer to the info about the opcode. NULL if the opcode was + * not found in the table. + * + * DESCRIPTION: Find AML opcode description based on the opcode + * + ******************************************************************************/ + +ACPI_OP_INFO * +acpi_ps_get_opcode_info ( + u16 opcode) +{ + ACPI_OP_INFO *op; + s32 hash; + + + /* compute hash/index into the Acpi_aml_op_index table */ + + switch (opcode >> 8) + { + case 0: + + /* Simple (8-bit) opcode */ + + hash = opcode; + break; + + + case AML_EXTOP: + + /* Extended (16-bit, prefix+opcode) opcode */ + + hash = (opcode + AML_EXTOP_HASH_OFFSET) & 0xff; + break; + + + case AML_LNOT_OP: + + /* This case is for the bogus opcodes LNOTEQUAL, LLESSEQUAL, LGREATEREQUAL */ + + hash = (opcode + AML_LNOT_HASH_OFFSET) & 0xff; + break; + + + default: + + return NULL; + } + + + /* Get the Op info pointer for this opcode */ + + op = &acpi_gbl_aml_op_info [(s32) acpi_gbl_aml_op_info_index [hash]]; + + + /* If the returned opcode matches, we have a valid opcode */ + + if (op->opcode == opcode) { + return op; + } + + /* Otherwise, the opcode is an ASCII char or other non-opcode value */ + + return NULL; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_opcode_name + * + * PARAMETERS: Opcode - The AML opcode + * + * RETURN: A pointer to the name of the opcode (ASCII String) + * Note: Never returns NULL. + * + * DESCRIPTION: Translate an opcode into a human-readable string + * + ******************************************************************************/ + +char * +acpi_ps_get_opcode_name ( + u16 opcode) +{ + ACPI_OP_INFO *op; + + + op = acpi_ps_get_opcode_info (opcode); + + if (!op) { + return "*ERROR*"; + } + + DEBUG_ONLY_MEMBERS (return op->name); + return "AE_NOT_CONFIGURED"; +} + + +/******************************************************************************* + * + * NAME: Acpi_gbl_Aml_op_info + * + * DESCRIPTION: Opcode table. Each entry contains <opcode, type, name, operands> + * The name is a simple ascii string, the operand specifier is an + * ascii string with one letter per operand. The letter specifies + * the operand type. + * + ******************************************************************************/ + + +/* + * Flags byte: 0-4 (5 bits) = Opcode Type + * 5 (1 bit) = Has arguments flag + * 6-7 (2 bits) = Reserved + */ +#define AML_NO_ARGS 0 +#define AML_HAS_ARGS OP_INFO_HAS_ARGS + +/* + * All AML opcodes and the parse-time arguments for each. Used by the AML parser Each list is compressed + * into a 32-bit number and stored in the master opcode table at the end of this file. + */ + +#define ARGP_ZERO_OP ARG_NONE +#define ARGP_ONE_OP ARG_NONE +#define ARGP_ALIAS_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_NAME) +#define ARGP_NAME_OP ARGP_LIST2 (ARGP_NAME, ARGP_DATAOBJ) +#define ARGP_BYTE_OP ARGP_LIST1 (ARGP_BYTEDATA) +#define ARGP_WORD_OP ARGP_LIST1 (ARGP_WORDDATA) +#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA) +#define ARGP_STRING_OP ARGP_LIST1 (ARGP_CHARLIST) +#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST) +#define ARGP_BUFFER_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_BYTELIST) +#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST) +#define ARGP_METHOD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMLIST) +#define ARGP_LOCAL0 ARG_NONE +#define ARGP_LOCAL1 ARG_NONE +#define ARGP_LOCAL2 ARG_NONE +#define ARGP_LOCAL3 ARG_NONE +#define ARGP_LOCAL4 ARG_NONE +#define ARGP_LOCAL5 ARG_NONE +#define ARGP_LOCAL6 ARG_NONE +#define ARGP_LOCAL7 ARG_NONE +#define ARGP_ARG0 ARG_NONE +#define ARGP_ARG1 ARG_NONE +#define ARGP_ARG2 ARG_NONE +#define ARGP_ARG3 ARG_NONE +#define ARGP_ARG4 ARG_NONE +#define ARGP_ARG5 ARG_NONE +#define ARGP_ARG6 ARG_NONE +#define ARGP_STORE_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SUPERNAME) +#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_ADD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SUBTRACT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_INCREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_DECREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_MULTIPLY_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_DIVIDE_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET, ARGP_TARGET) +#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_AND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NAND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_OR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_XOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FIND_SET_RIGHT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_DEREF_OF_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_SIZE_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_INDEX_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MATCH_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_WORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_LAND_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LNOT_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_LEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESS_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_IF_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST) +#define ARGP_WHILE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_NOOP_CODE ARG_NONE +#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_BREAK_OP ARG_NONE +#define ARGP_BREAK_POINT_OP ARG_NONE +#define ARGP_ONES_OP ARG_NONE +#define ARGP_MUTEX_OP ARGP_LIST2 (ARGP_NAME, ARGP_BYTEDATA) +#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME) +#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) +#define ARGP_CREATE_FIELD_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_LOAD_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_SUPERNAME) +#define ARGP_STALL_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_SLEEP_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_ACQUIRE_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_WORDDATA) +#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_RESET_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_FROM_BCDOP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_BCDOP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_UN_LOAD_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_REVISION_OP ARG_NONE +#define ARGP_DEBUG_OP ARG_NONE +#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG) +#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_DEF_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_DEVICE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST) +#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST) +#define ARGP_THERMAL_ZONE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_INDEX_FIELD_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_BANK_FIELD_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_TERMARG, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_LNOTEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESSEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATEREQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_METHODCALL_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NAMEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_ACCESSFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_STATICSTRING_OP ARGP_LIST1 (ARGP_NAMESTRING) + + +/* + * All AML opcodes and the runtime arguments for each. Used by the AML interpreter Each list is compressed + * into a 32-bit number and stored in the master opcode table at the end of this file. + * + * (Used by Acpi_aml_prep_operands procedure) + */ + +#define ARGI_ZERO_OP ARG_NONE +#define ARGI_ONE_OP ARG_NONE +#define ARGI_ALIAS_OP ARGI_INVALID_OPCODE +#define ARGI_NAME_OP ARGI_INVALID_OPCODE +#define ARGI_BYTE_OP ARGI_INVALID_OPCODE +#define ARGI_WORD_OP ARGI_INVALID_OPCODE +#define ARGI_DWORD_OP ARGI_INVALID_OPCODE +#define ARGI_STRING_OP ARGI_INVALID_OPCODE +#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE +#define ARGI_BUFFER_OP ARGI_INVALID_OPCODE +#define ARGI_PACKAGE_OP ARGI_INVALID_OPCODE +#define ARGI_METHOD_OP ARGI_INVALID_OPCODE +#define ARGI_LOCAL0 ARG_NONE +#define ARGI_LOCAL1 ARG_NONE +#define ARGI_LOCAL2 ARG_NONE +#define ARGI_LOCAL3 ARG_NONE +#define ARGI_LOCAL4 ARG_NONE +#define ARGI_LOCAL5 ARG_NONE +#define ARGI_LOCAL6 ARG_NONE +#define ARGI_LOCAL7 ARG_NONE +#define ARGI_ARG0 ARG_NONE +#define ARGI_ARG1 ARG_NONE +#define ARGI_ARG2 ARG_NONE +#define ARGI_ARG3 ARG_NONE +#define ARGI_ARG4 ARG_NONE +#define ARGI_ARG5 ARG_NONE +#define ARGI_ARG6 ARG_NONE +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_TARGETREF) +#define ARGI_REF_OF_OP ARGI_LIST1 (ARGI_REFERENCE) +#define ARGI_ADD_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_TARGETREF) +#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_REFERENCE) +#define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_REFERENCE) +#define ARGI_MULTIPLY_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_DIVIDE_OP ARGI_LIST4 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF, ARGI_TARGETREF) +#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_OR_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_XOR_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_BIT_NOT_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REFERENCE) +#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_REFERENCE, ARGI_NUMBER) +#define ARGI_SIZE_OF_OP ARGI_LIST1 (ARGI_DATAOBJECT) +#define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_MATCH_OP ARGI_LIST6 (ARGI_PACKAGE, ARGI_NUMBER, ARGI_NUMBER, ARGI_NUMBER, ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_DWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_NUMBER, ARGI_REFERENCE) +#define ARGI_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_NUMBER, ARGI_REFERENCE) +#define ARGI_BYTE_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_NUMBER, ARGI_REFERENCE) +#define ARGI_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_NUMBER, ARGI_REFERENCE) +#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE) +#define ARGI_LAND_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_LOR_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_LNOT_OP ARGI_LIST1 (ARGI_NUMBER) +#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_IF_OP ARGI_INVALID_OPCODE +#define ARGI_ELSE_OP ARGI_INVALID_OPCODE +#define ARGI_WHILE_OP ARGI_INVALID_OPCODE +#define ARGI_NOOP_CODE ARG_NONE +#define ARGI_RETURN_OP ARGI_INVALID_OPCODE +#define ARGI_BREAK_OP ARG_NONE +#define ARGI_BREAK_POINT_OP ARG_NONE +#define ARGI_ONES_OP ARG_NONE +#define ARGI_MUTEX_OP ARGI_INVALID_OPCODE +#define ARGI_EVENT_OP ARGI_INVALID_OPCODE +#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_REFERENCE, ARGI_TARGETREF) +#define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_NUMBER, ARGI_NUMBER, ARGI_REFERENCE) +#define ARGI_LOAD_OP ARGI_LIST2 (ARGI_REGION, ARGI_TARGETREF) +#define ARGI_STALL_OP ARGI_LIST1 (ARGI_NUMBER) +#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_NUMBER) +#define ARGI_ACQUIRE_OP ARGI_LIST2 (ARGI_MUTEX, ARGI_NUMBER) +#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_NUMBER) +#define ARGI_RESET_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_RELEASE_OP ARGI_LIST1 (ARGI_MUTEX) +#define ARGI_FROM_BCDOP ARGI_LIST2 (ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_TO_BCDOP ARGI_LIST2 (ARGI_NUMBER, ARGI_TARGETREF) +#define ARGI_UN_LOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE) +#define ARGI_REVISION_OP ARG_NONE +#define ARGI_DEBUG_OP ARG_NONE +#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_NUMBER, ARGI_NUMBER, ARGI_NUMBER) +#define ARGI_REGION_OP ARGI_INVALID_OPCODE +#define ARGI_DEF_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_DEVICE_OP ARGI_INVALID_OPCODE +#define ARGI_PROCESSOR_OP ARGI_INVALID_OPCODE +#define ARGI_POWER_RES_OP ARGI_INVALID_OPCODE +#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE +#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE +#define ARGI_METHODCALL_OP ARGI_INVALID_OPCODE +#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE +#define ARGI_RESERVEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_ACCESSFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE + + +/* + * Master Opcode information table. A summary of everything we know about each opcode, all in one place. + */ + + +ACPI_OP_INFO acpi_gbl_aml_op_info[] = +{ +/* Opcode Opcode Type Has Arguments? Child Name Parser Args Interpreter Args */ + +/* 00 */ OP_INFO_ENTRY (AML_ZERO_OP, OPTYPE_CONSTANT| AML_NO_ARGS| 0, "Zero_op", ARGP_ZERO_OP, ARGI_ZERO_OP), +/* 01 */ OP_INFO_ENTRY (AML_ONE_OP, OPTYPE_CONSTANT| AML_NO_ARGS| 0, "One_op", ARGP_ONE_OP, ARGI_ONE_OP), +/* 02 */ OP_INFO_ENTRY (AML_ALIAS_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP), +/* 03 */ OP_INFO_ENTRY (AML_NAME_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Name", ARGP_NAME_OP, ARGI_NAME_OP), +/* 04 */ OP_INFO_ENTRY (AML_BYTE_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "Byte_const", ARGP_BYTE_OP, ARGI_BYTE_OP), +/* 05 */ OP_INFO_ENTRY (AML_WORD_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "Word_const", ARGP_WORD_OP, ARGI_WORD_OP), +/* 06 */ OP_INFO_ENTRY (AML_DWORD_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "Dword_const", ARGP_DWORD_OP, ARGI_DWORD_OP), +/* 07 */ OP_INFO_ENTRY (AML_STRING_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "String", ARGP_STRING_OP, ARGI_STRING_OP), +/* 08 */ OP_INFO_ENTRY (AML_SCOPE_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP), +/* 09 */ OP_INFO_ENTRY (AML_BUFFER_OP, OPTYPE_DATA_TERM| AML_HAS_ARGS| 0, "Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP), +/* 0A */ OP_INFO_ENTRY (AML_PACKAGE_OP, OPTYPE_DATA_TERM| AML_HAS_ARGS| 0, "Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP), +/* 0B */ OP_INFO_ENTRY (AML_METHOD_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Method", ARGP_METHOD_OP, ARGI_METHOD_OP), +/* 0C */ OP_INFO_ENTRY (AML_LOCAL0, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local0", ARGP_LOCAL0, ARGI_LOCAL0), +/* 0D */ OP_INFO_ENTRY (AML_LOCAL1, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local1", ARGP_LOCAL1, ARGI_LOCAL1), +/* 0E */ OP_INFO_ENTRY (AML_LOCAL2, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local2", ARGP_LOCAL2, ARGI_LOCAL2), +/* 0F */ OP_INFO_ENTRY (AML_LOCAL3, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local3", ARGP_LOCAL3, ARGI_LOCAL3), +/* 10 */ OP_INFO_ENTRY (AML_LOCAL4, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local4", ARGP_LOCAL4, ARGI_LOCAL4), +/* 11 */ OP_INFO_ENTRY (AML_LOCAL5, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local5", ARGP_LOCAL5, ARGI_LOCAL5), +/* 12 */ OP_INFO_ENTRY (AML_LOCAL6, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local6", ARGP_LOCAL6, ARGI_LOCAL6), +/* 13 */ OP_INFO_ENTRY (AML_LOCAL7, OPTYPE_LOCAL_VARIABLE| AML_NO_ARGS| 0, "Local7", ARGP_LOCAL7, ARGI_LOCAL7), +/* 14 */ OP_INFO_ENTRY (AML_ARG0, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg0", ARGP_ARG0, ARGI_ARG0), +/* 15 */ OP_INFO_ENTRY (AML_ARG1, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg1", ARGP_ARG1, ARGI_ARG1), +/* 16 */ OP_INFO_ENTRY (AML_ARG2, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg2", ARGP_ARG2, ARGI_ARG2), +/* 17 */ OP_INFO_ENTRY (AML_ARG3, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg3", ARGP_ARG3, ARGI_ARG3), +/* 18 */ OP_INFO_ENTRY (AML_ARG4, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg4", ARGP_ARG4, ARGI_ARG4), +/* 19 */ OP_INFO_ENTRY (AML_ARG5, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg5", ARGP_ARG5, ARGI_ARG5), +/* 1_a */ OP_INFO_ENTRY (AML_ARG6, OPTYPE_METHOD_ARGUMENT| AML_NO_ARGS| 0, "Arg6", ARGP_ARG6, ARGI_ARG6), +/* 1_b */ OP_INFO_ENTRY (AML_STORE_OP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "Store", ARGP_STORE_OP, ARGI_STORE_OP), +/* 1_c */ OP_INFO_ENTRY (AML_REF_OF_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Ref_of", ARGP_REF_OF_OP, ARGI_REF_OF_OP), +/* 1_d */ OP_INFO_ENTRY (AML_ADD_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Add", ARGP_ADD_OP, ARGI_ADD_OP), +/* 1_e */ OP_INFO_ENTRY (AML_CONCAT_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Concat", ARGP_CONCAT_OP, ARGI_CONCAT_OP), +/* 1_f */ OP_INFO_ENTRY (AML_SUBTRACT_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP), +/* 20 */ OP_INFO_ENTRY (AML_INCREMENT_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP), +/* 21 */ OP_INFO_ENTRY (AML_DECREMENT_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP), +/* 22 */ OP_INFO_ENTRY (AML_MULTIPLY_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP), +/* 23 */ OP_INFO_ENTRY (AML_DIVIDE_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP), +/* 24 */ OP_INFO_ENTRY (AML_SHIFT_LEFT_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Shift_left", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP), +/* 25 */ OP_INFO_ENTRY (AML_SHIFT_RIGHT_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Shift_right", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP), +/* 26 */ OP_INFO_ENTRY (AML_BIT_AND_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP), +/* 27 */ OP_INFO_ENTRY (AML_BIT_NAND_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP), +/* 28 */ OP_INFO_ENTRY (AML_BIT_OR_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP), +/* 29 */ OP_INFO_ENTRY (AML_BIT_NOR_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP), +/* 2_a */ OP_INFO_ENTRY (AML_BIT_XOR_OP, OPTYPE_DYADIC2_r| AML_HAS_ARGS| 0, "XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP), +/* 2_b */ OP_INFO_ENTRY (AML_BIT_NOT_OP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP), +/* 2_c */ OP_INFO_ENTRY (AML_FIND_SET_LEFT_BIT_OP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "Find_set_left_bit", ARGP_FIND_SET_LEFT_BIT_OP, ARGI_FIND_SET_LEFT_BIT_OP), +/* 2_d */ OP_INFO_ENTRY (AML_FIND_SET_RIGHT_BIT_OP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "Find_set_right_bit", ARGP_FIND_SET_RIGHT_BIT_OP, ARGI_FIND_SET_RIGHT_BIT_OP), +/* 2_e */ OP_INFO_ENTRY (AML_DEREF_OF_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Deref_of", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP), +/* 2_f */ OP_INFO_ENTRY (AML_NOTIFY_OP, OPTYPE_DYADIC1| AML_HAS_ARGS| 0, "Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP), +/* 30 */ OP_INFO_ENTRY (AML_SIZE_OF_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Size_of", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP), +/* 31 */ OP_INFO_ENTRY (AML_INDEX_OP, OPTYPE_INDEX| AML_HAS_ARGS| 0, "Index", ARGP_INDEX_OP, ARGI_INDEX_OP), +/* 32 */ OP_INFO_ENTRY (AML_MATCH_OP, OPTYPE_MATCH| AML_HAS_ARGS| 0, "Match", ARGP_MATCH_OP, ARGI_MATCH_OP), +/* 33 */ OP_INFO_ENTRY (AML_DWORD_FIELD_OP, OPTYPE_CREATE_FIELD| AML_HAS_ARGS| 0, "Create_dWord_field", ARGP_DWORD_FIELD_OP, ARGI_DWORD_FIELD_OP), +/* 34 */ OP_INFO_ENTRY (AML_WORD_FIELD_OP, OPTYPE_CREATE_FIELD| AML_HAS_ARGS| 0, "Create_word_field", ARGP_WORD_FIELD_OP, ARGI_WORD_FIELD_OP), +/* 35 */ OP_INFO_ENTRY (AML_BYTE_FIELD_OP, OPTYPE_CREATE_FIELD| AML_HAS_ARGS| 0, "Create_byte_field", ARGP_BYTE_FIELD_OP, ARGI_BYTE_FIELD_OP), +/* 36 */ OP_INFO_ENTRY (AML_BIT_FIELD_OP, OPTYPE_CREATE_FIELD| AML_HAS_ARGS| 0, "Create_bit_field", ARGP_BIT_FIELD_OP, ARGI_BIT_FIELD_OP), +/* 37 */ OP_INFO_ENTRY (AML_TYPE_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "Object_type", ARGP_TYPE_OP, ARGI_TYPE_OP), +/* 38 */ OP_INFO_ENTRY (AML_LAND_OP, OPTYPE_DYADIC2| AML_HAS_ARGS| 0, "LAnd", ARGP_LAND_OP, ARGI_LAND_OP), +/* 39 */ OP_INFO_ENTRY (AML_LOR_OP, OPTYPE_DYADIC2| AML_HAS_ARGS| 0, "LOr", ARGP_LOR_OP, ARGI_LOR_OP), +/* 3_a */ OP_INFO_ENTRY (AML_LNOT_OP, OPTYPE_MONADIC2| AML_HAS_ARGS| 0, "LNot", ARGP_LNOT_OP, ARGI_LNOT_OP), +/* 3_b */ OP_INFO_ENTRY (AML_LEQUAL_OP, OPTYPE_DYADIC2| AML_HAS_ARGS| 0, "LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP), +/* 3_c */ OP_INFO_ENTRY (AML_LGREATER_OP, OPTYPE_DYADIC2| AML_HAS_ARGS| 0, "LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP), +/* 3_d */ OP_INFO_ENTRY (AML_LLESS_OP, OPTYPE_DYADIC2| AML_HAS_ARGS| 0, "LLess", ARGP_LLESS_OP, ARGI_LLESS_OP), +/* 3_e */ OP_INFO_ENTRY (AML_IF_OP, OPTYPE_CONTROL| AML_HAS_ARGS| 0, "If", ARGP_IF_OP, ARGI_IF_OP), +/* 3_f */ OP_INFO_ENTRY (AML_ELSE_OP, OPTYPE_CONTROL| AML_HAS_ARGS| 0, "Else", ARGP_ELSE_OP, ARGI_ELSE_OP), +/* 40 */ OP_INFO_ENTRY (AML_WHILE_OP, OPTYPE_CONTROL| AML_HAS_ARGS| 0, "While", ARGP_WHILE_OP, ARGI_WHILE_OP), +/* 41 */ OP_INFO_ENTRY (AML_NOOP_CODE, OPTYPE_CONTROL| AML_NO_ARGS| 0, "Noop", ARGP_NOOP_CODE, ARGI_NOOP_CODE), +/* 42 */ OP_INFO_ENTRY (AML_RETURN_OP, OPTYPE_CONTROL| AML_HAS_ARGS| 0, "Return", ARGP_RETURN_OP, ARGI_RETURN_OP), +/* 43 */ OP_INFO_ENTRY (AML_BREAK_OP, OPTYPE_CONTROL| AML_NO_ARGS| 0, "Break", ARGP_BREAK_OP, ARGI_BREAK_OP), +/* 44 */ OP_INFO_ENTRY (AML_BREAK_POINT_OP, OPTYPE_CONTROL| AML_NO_ARGS| 0, "Break_point", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP), +/* 45 */ OP_INFO_ENTRY (AML_ONES_OP, OPTYPE_CONSTANT| AML_NO_ARGS| 0, "Ones_op", ARGP_ONES_OP, ARGI_ONES_OP), + +/* Prefixed opcodes (Two-byte opcodes with a prefix op) */ + +/* 46 */ OP_INFO_ENTRY (AML_MUTEX_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP), +/* 47 */ OP_INFO_ENTRY (AML_EVENT_OP, OPTYPE_NAMED_OBJECT| AML_NO_ARGS| 0, "Event", ARGP_EVENT_OP, ARGI_EVENT_OP), +/* 48 */ OP_INFO_ENTRY (AML_COND_REF_OF_OP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "Cond_ref_of", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP), +/* 49 */ OP_INFO_ENTRY (AML_CREATE_FIELD_OP, OPTYPE_CREATE_FIELD| AML_HAS_ARGS| 0, "Create_field", ARGP_CREATE_FIELD_OP, ARGI_CREATE_FIELD_OP), +/* 4_a */ OP_INFO_ENTRY (AML_LOAD_OP, OPTYPE_RECONFIGURATION| AML_HAS_ARGS| 0, "Load", ARGP_LOAD_OP, ARGI_LOAD_OP), +/* 4_b */ OP_INFO_ENTRY (AML_STALL_OP, OPTYPE_MONADIC1| AML_HAS_ARGS| 0, "Stall", ARGP_STALL_OP, ARGI_STALL_OP), +/* 4_c */ OP_INFO_ENTRY (AML_SLEEP_OP, OPTYPE_MONADIC1| AML_HAS_ARGS| 0, "Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP), +/* 4_d */ OP_INFO_ENTRY (AML_ACQUIRE_OP, OPTYPE_DYADIC2_s| AML_HAS_ARGS| 0, "Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP), +/* 4_e */ OP_INFO_ENTRY (AML_SIGNAL_OP, OPTYPE_MONADIC1| AML_HAS_ARGS| 0, "Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP), +/* 4_f */ OP_INFO_ENTRY (AML_WAIT_OP, OPTYPE_DYADIC2_s| AML_HAS_ARGS| 0, "Wait", ARGP_WAIT_OP, ARGI_WAIT_OP), +/* 50 */ OP_INFO_ENTRY (AML_RESET_OP, OPTYPE_MONADIC1| AML_HAS_ARGS| 0, "Reset", ARGP_RESET_OP, ARGI_RESET_OP), +/* 51 */ OP_INFO_ENTRY (AML_RELEASE_OP, OPTYPE_MONADIC1| AML_HAS_ARGS| 0, "Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP), +/* 52 */ OP_INFO_ENTRY (AML_FROM_BCDOP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "From_bCD", ARGP_FROM_BCDOP, ARGI_FROM_BCDOP), +/* 53 */ OP_INFO_ENTRY (AML_TO_BCDOP, OPTYPE_MONADIC2_r| AML_HAS_ARGS| 0, "To_bCD", ARGP_TO_BCDOP, ARGI_TO_BCDOP), +/* 54 */ OP_INFO_ENTRY (AML_UN_LOAD_OP, OPTYPE_RECONFIGURATION| AML_HAS_ARGS| 0, "Unload", ARGP_UN_LOAD_OP, ARGI_UN_LOAD_OP), +/* 55 */ OP_INFO_ENTRY (AML_REVISION_OP, OPTYPE_CONSTANT| AML_NO_ARGS| 0, "Revision", ARGP_REVISION_OP, ARGI_REVISION_OP), +/* 56 */ OP_INFO_ENTRY (AML_DEBUG_OP, OPTYPE_CONSTANT| AML_NO_ARGS| 0, "Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP), +/* 57 */ OP_INFO_ENTRY (AML_FATAL_OP, OPTYPE_FATAL| AML_HAS_ARGS| 0, "Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP), +/* 58 */ OP_INFO_ENTRY (AML_REGION_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Op_region", ARGP_REGION_OP, ARGI_REGION_OP), +/* 59 */ OP_INFO_ENTRY (AML_DEF_FIELD_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Field", ARGP_DEF_FIELD_OP, ARGI_DEF_FIELD_OP), +/* 5_a */ OP_INFO_ENTRY (AML_DEVICE_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP), +/* 5_b */ OP_INFO_ENTRY (AML_PROCESSOR_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP), +/* 5_c */ OP_INFO_ENTRY (AML_POWER_RES_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Power_res", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP), +/* 5_d */ OP_INFO_ENTRY (AML_THERMAL_ZONE_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Thermal_zone", ARGP_THERMAL_ZONE_OP, ARGI_THERMAL_ZONE_OP), +/* 5_e */ OP_INFO_ENTRY (AML_INDEX_FIELD_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Index_field", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP), +/* 5_f */ OP_INFO_ENTRY (AML_BANK_FIELD_OP, OPTYPE_NAMED_OBJECT| AML_HAS_ARGS| 0, "Bank_field", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP), + +/* Internal opcodes that map to invalid AML opcodes */ + +/* 60 */ OP_INFO_ENTRY (AML_LNOTEQUAL_OP, OPTYPE_BOGUS| AML_HAS_ARGS| 0, "LNot_equal", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP), +/* 61 */ OP_INFO_ENTRY (AML_LLESSEQUAL_OP, OPTYPE_BOGUS| AML_HAS_ARGS| 0, "LLess_equal", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP), +/* 62 */ OP_INFO_ENTRY (AML_LGREATEREQUAL_OP, OPTYPE_BOGUS| AML_HAS_ARGS| 0, "LGreater_equal", ARGP_LGREATEREQUAL_OP, ARGI_LGREATEREQUAL_OP), +/* 63 */ OP_INFO_ENTRY (AML_NAMEPATH_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "Name_path", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP), +/* 64 */ OP_INFO_ENTRY (AML_METHODCALL_OP, OPTYPE_METHOD_CALL| AML_HAS_ARGS| 0, "Method_call", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP), +/* 65 */ OP_INFO_ENTRY (AML_BYTELIST_OP, OPTYPE_LITERAL| AML_NO_ARGS| 0, "Byte_list", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP), +/* 66 */ OP_INFO_ENTRY (AML_RESERVEDFIELD_OP, OPTYPE_BOGUS| AML_NO_ARGS| 0, "Reserved_field", ARGP_RESERVEDFIELD_OP, ARGI_RESERVEDFIELD_OP), +/* 67 */ OP_INFO_ENTRY (AML_NAMEDFIELD_OP, OPTYPE_BOGUS| AML_NO_ARGS| 0, "Named_field", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP), +/* 68 */ OP_INFO_ENTRY (AML_ACCESSFIELD_OP, OPTYPE_BOGUS| AML_NO_ARGS| 0, "Access_field", ARGP_ACCESSFIELD_OP, ARGI_ACCESSFIELD_OP), +/* 69 */ OP_INFO_ENTRY (AML_STATICSTRING_OP, OPTYPE_BOGUS| AML_NO_ARGS| 0, "Static_string", ARGP_STATICSTRING_OP, ARGI_STATICSTRING_OP), +/* 6_a */ OP_INFO_ENTRY (0, OPTYPE_BOGUS| AML_HAS_ARGS| 0, "UNKNOWN_OP!", ARG_NONE, ARG_NONE), + OP_INFO_ENTRY (0, 0| AML_HAS_ARGS| 0, NULL, ARG_NONE, ARG_NONE) +}; + +#define _UNK 0x6A +#define _UNKNOWN_OPCODE 0x02 /* An example unknown opcode */ + +/* + * This table is directly indexed by the opcodes, and returns an + * index into the table above + */ + +u8 acpi_gbl_aml_op_info_index[256] = +{ +/* 0 1 2 3 4 5 6 7 */ +/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK, +/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, _UNK, _UNK, +/* 0x10 */ 0x08, 0x09, 0x0a, _UNK, 0x0b, _UNK, _UNK, 0x46, +/* 0x18 */ 0x47, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x28 */ 0x48, 0x49, _UNK, _UNK, _UNK, 0x63, _UNK, _UNK, +/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x4a, 0x4b, +/* 0x38 */ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, +/* 0x40 */ 0x54, _UNK, _UNK, _UNK, _UNK, _UNK, 0x55, 0x56, +/* 0x48 */ 0x57, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, +/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK, +/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, +/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, +/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, _UNK, _UNK, 0x2f, 0x30, +/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, _UNK, +/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x58, 0x59, +/* 0x98 */ 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, _UNK, _UNK, +/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61, +/* 0xA8 */ 0x62, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK, +/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45, +}; diff --git a/drivers/acpi/parser/psparse.c b/drivers/acpi/parser/psparse.c new file mode 100644 index 000000000..7ec1e70fa --- /dev/null +++ b/drivers/acpi/parser/psparse.c @@ -0,0 +1,726 @@ +/****************************************************************************** + * + * Module Name: psparse - Parser top level AML parse routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Parse the AML and build an operation tree as most interpreters, + * like Perl, do. Parsing is done by hand rather than with a YACC + * generated parser to tightly constrain stack and dynamic memory + * usage. At the same time, parsing is kept flexible and the code + * fairly compact by parsing based on a list of AML opcode + * templates in Acpi_gbl_Aml_op_info[] + */ + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "amlcode.h" +#include "namesp.h" +#include "debugger.h" + +#define _COMPONENT PARSER + MODULE_NAME ("psparse"); + + +u32 acpi_gbl_depth = 0; +extern u32 acpi_gbl_scope_depth; + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_delete_completed_op + * + * PARAMETERS: State - Walk state + * Op - Completed op + * + * RETURN: AE_OK + * + * DESCRIPTION: Callback function for Acpi_ps_get_next_walk_op(). Used during + * Acpi_ps_delete_parse tree to delete Op objects when all sub-objects + * have been visited (and deleted.) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_delete_completed_op ( + ACPI_WALK_STATE *state, + ACPI_GENERIC_OP *op) +{ + + acpi_ps_free_op (op); + return AE_OK; +} + + +#ifndef PARSER_ONLY +/******************************************************************************* + * + * FUNCTION: Acpi_ps_delete_parse_tree + * + * PARAMETERS: Root - Root of tree (or subtree) to delete + * + * RETURN: None + * + * DESCRIPTION: Delete a portion of or an entire parse tree. + * + ******************************************************************************/ + +void +acpi_ps_delete_parse_tree ( + ACPI_GENERIC_OP *root) +{ + ACPI_GENERIC_OP *op; + ACPI_WALK_STATE walk_state; + + + walk_state.origin = root; + op = root; + + /* TBD: [Restructure] hack for root case */ + + if (op == acpi_gbl_parsed_namespace_root) { + op = acpi_ps_get_child (op); + } + + /* Save root until last, so that we know when the tree has been walked */ + + walk_state.next_op = op; + walk_state.next_op_info = NEXT_OP_DOWNWARD; + + while (walk_state.next_op) { + acpi_ps_get_next_walk_op (&walk_state, walk_state.next_op, + acpi_ps_delete_completed_op); + } +} +#endif + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_peek_opcode + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Get next AML opcode (without incrementing AML pointer) + * + ******************************************************************************/ + +u32 +acpi_ps_get_opcode_size ( + u32 opcode) +{ + + /* Extended (2-byte) opcode if > 255 */ + + if (opcode > 0x00FF) { + return 2; + } + + /* Otherwise, just a single byte opcode */ + + return 1; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_peek_opcode + * + * PARAMETERS: Parser_state - A parser state object + * + * RETURN: Status + * + * DESCRIPTION: Get next AML opcode (without incrementing AML pointer) + * + ******************************************************************************/ + +u16 +acpi_ps_peek_opcode ( + ACPI_PARSE_STATE *parser_state) +{ + u8 *aml; + u16 opcode; + + + aml = parser_state->aml; + opcode = (u16) GET8 (aml); + + aml++; + + + /* + * Original code special cased LNOTEQUAL, LLESSEQUAL, LGREATEREQUAL. + * These opcodes are no longer recognized. Instead, they are broken into + * two opcodes. + * + * + * if (Opcode == AML_EXTOP + * || (Opcode == AML_LNOT + * && (GET8 (Acpi_aml) == AML_LEQUAL + * || GET8 (Acpi_aml) == AML_LGREATER + * || GET8 (Acpi_aml) == AML_LLESS))) + * + * extended Opcode, !=, <=, or >= + */ + + if (opcode == AML_EXTOP) { + /* Extended opcode */ + + opcode = (u16) ((opcode << 8) | GET8 (aml)); + aml++; + } + + /* don't convert bare name to a namepath */ + + return opcode; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_create_state + * + * PARAMETERS: Acpi_aml - Acpi_aml code pointer + * Acpi_aml_size - Length of AML code + * + * RETURN: A new parser state object + * + * DESCRIPTION: Create and initialize a new parser state object + * + ******************************************************************************/ + +ACPI_PARSE_STATE * +acpi_ps_create_state ( + u8 *aml, + s32 aml_size) +{ + ACPI_PARSE_STATE *parser_state; + + + parser_state = acpi_cm_callocate (sizeof (ACPI_PARSE_STATE)); + if (!parser_state) { + return (NULL); + } + + parser_state->aml = aml; + parser_state->aml_end = aml + aml_size; + parser_state->pkg_end = parser_state->aml_end; + parser_state->aml_start = aml; + + + return (parser_state); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_find_object + * + * PARAMETERS: Opcode - Current opcode + * Parser_state - Current state + * Walk_state - Current state + * *Op - Where found/new op is returned + * + * RETURN: Status + * + * DESCRIPTION: Find a named object. Two versions - one to search the parse + * tree (for parser-only applications such as acpidump), another + * to search the ACPI internal namespace (the parse tree may no + * longer exist) + * + ******************************************************************************/ + +#ifdef PARSER_ONLY + +ACPI_STATUS +acpi_ps_find_object ( + u16 opcode, + ACPI_PARSE_STATE *parser_state, + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP **op) +{ + char *path; + + + /* Find the name in the parse tree */ + + path = acpi_ps_get_next_namestring (parser_state); + + *op = acpi_ps_find (acpi_ps_get_parent_scope (parser_state), + path, opcode, 1); + + if (!(*op)) { + return AE_NOT_FOUND; + } + + return AE_OK; +} +#else + +ACPI_STATUS +acpi_ps_find_object ( + u16 opcode, + ACPI_PARSE_STATE *parser_state, + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP **out_op) +{ + char *path; + ACPI_GENERIC_OP *op; + OBJECT_TYPE_INTERNAL data_type; + ACPI_STATUS status; + ACPI_NAMED_OBJECT *entry = NULL; + + + /* + * The full parse tree has already been deleted -- therefore, we are parsing + * a control method. We can lookup the name in the namespace instead of + * the parse tree! + */ + + + path = acpi_ps_get_next_namestring (parser_state); + + /* Map the raw opcode into an internal object type */ + + data_type = acpi_ds_map_named_opcode_to_data_type (opcode); + + /* + * Enter the object into the namespace + * LOAD_PASS1 means Create if not found + */ + + status = acpi_ns_lookup (walk_state->scope_info, path, data_type, + IMODE_LOAD_PASS1, + NS_NO_UPSEARCH, walk_state, &entry); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Create a new op */ + + op = acpi_ps_alloc_op (opcode); + if (!op) { + return (AE_NO_MEMORY); + } + + /* Initialize */ + + ((ACPI_NAMED_OP *)op)->name = entry->name; + op->acpi_named_object = entry; + + + acpi_ps_append_arg (acpi_ps_get_parent_scope (parser_state), op); + + *out_op = op; + + + return (AE_OK); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_parse_loop + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Status + * + * DESCRIPTION: Parse AML (pointed to by the current parser state) and return + * a tree of ops. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_parse_loop ( + ACPI_PARSE_STATE *parser_state, + ACPI_WALK_STATE *walk_state, + u32 parse_flags) +{ + ACPI_STATUS status = AE_OK; + ACPI_GENERIC_OP *op = NULL; /* current op */ + ACPI_OP_INFO *op_info; + ACPI_GENERIC_OP *arg = NULL; + ACPI_DEFERRED_OP *deferred_op; + u32 arg_count; /* push for fixed or var args */ + u32 arg_types = 0; + ACPI_PTRDIFF aml_offset; + u16 opcode; + ACPI_GENERIC_OP pre_op; + + +#ifndef PARSER_ONLY + OBJECT_TYPE_INTERNAL data_type; +#endif + + + /* + * Iterative parsing loop, while there is more aml to process: + */ + while (parser_state->aml < parser_state->aml_end) { + if (!op) { + /* Get the next opcode from the AML stream */ + + aml_offset = parser_state->aml - parser_state->aml_start; + opcode = acpi_ps_peek_opcode (parser_state); + op_info = acpi_ps_get_opcode_info (opcode); + + /* + * First cut to determine what we have found: + * 1) A valid AML opcode + * 2) A name string + * 3) An unknown/invalid opcode + */ + + if (op_info) { + /* Found opcode info, this is a normal opcode */ + + parser_state->aml += acpi_ps_get_opcode_size (opcode); + arg_types = op_info->parse_args; + } + + else if (acpi_ps_is_prefix_char (opcode) || + acpi_ps_is_leading_char (opcode)) + { + /* + * Starts with a valid prefix or ASCII char, this is a name + * string. Convert the bare name string to a namepath. + */ + + opcode = AML_NAMEPATH_OP; + arg_types = ARGP_NAMESTRING; + } + + else { + /* The opcode is unrecognized. Just skip unknown opcodes */ + + parser_state->aml += acpi_ps_get_opcode_size (opcode); + continue; + } + + + /* Create Op structure and append to parent's argument list */ + + if (acpi_ps_is_named_op (opcode)) { + pre_op.value.arg = NULL; + pre_op.opcode = opcode; + + while (GET_CURRENT_ARG_TYPE (arg_types) != ARGP_NAME) { + arg = acpi_ps_get_next_arg (parser_state, + GET_CURRENT_ARG_TYPE (arg_types), + &arg_count); + acpi_ps_append_arg (&pre_op, arg); + INCREMENT_ARG_LIST (arg_types); + } + + + /* We know that this arg is a name, move to next arg */ + + INCREMENT_ARG_LIST (arg_types); + + status = acpi_ps_find_object (opcode, parser_state, walk_state, &op); + if (ACPI_FAILURE (status)) { + return (AE_NOT_FOUND); + } + + acpi_ps_append_arg (op, pre_op.value.arg); + acpi_gbl_depth++; + + + if (op->opcode == AML_REGION_OP) { + deferred_op = acpi_ps_to_deferred_op (op); + if (deferred_op) { + /* + * Skip parsing of control method or opregion body, + * because we don't have enough info in the first pass + * to parse them correctly. + * + * Backup to beginning of Op_region declaration (2 for + * Opcode, 4 for name) + * + * Body_length is unknown until we parse the body + */ + + deferred_op->body = parser_state->aml - 6; + deferred_op->body_length = 0; + } + } + } + + else { + /* Not a named opcode, just allocate Op and append to parent */ + + op = acpi_ps_alloc_op (opcode); + if (!op) { + return (AE_NO_MEMORY); + } + + acpi_ps_append_arg (acpi_ps_get_parent_scope (parser_state), op); + } + + op->aml_offset = aml_offset; + + } + + + arg_count = 0; + if (arg_types) /* Are there any arguments that must be processed? */ { + /* get arguments */ + + switch (op->opcode) + { + case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + case AML_WORD_OP: /* AML_WORDDATA_ARG */ + case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + case AML_STRING_OP: /* AML_ASCIICHARLIST_ARG */ + + /* fill in constant or string argument directly */ + + acpi_ps_get_next_simple_arg (parser_state, + GET_CURRENT_ARG_TYPE (arg_types), op); + break; + + case AML_NAMEPATH_OP: /* AML_NAMESTRING_ARG */ + + acpi_ps_get_next_namepath (parser_state, op, &arg_count, 1); + arg_types = 0; + break; + + + default: + + /* Op is not a constant or string, append each argument */ + + while (GET_CURRENT_ARG_TYPE (arg_types) && !arg_count) { + aml_offset = parser_state->aml - parser_state->aml_start; + arg = acpi_ps_get_next_arg (parser_state, + GET_CURRENT_ARG_TYPE (arg_types), + &arg_count); + + if (arg) { + arg->aml_offset = aml_offset; + } + + acpi_ps_append_arg (op, arg); + INCREMENT_ARG_LIST (arg_types); + } + + + /* For a method, save the length and address of the body */ + + if (op->opcode == AML_METHOD_OP) { + deferred_op = acpi_ps_to_deferred_op (op); + if (deferred_op) { + /* + * Skip parsing of control method or opregion body, + * because we don't have enough info in the first pass + * to parse them correctly. + */ + + deferred_op->body = parser_state->aml; + deferred_op->body_length = parser_state->pkg_end - + parser_state->aml; + + /* + * Skip body of method. For Op_regions, we must continue + * parsing because the opregion is not a standalone + * package (We don't know where the end is). + */ + parser_state->aml = parser_state->pkg_end; + arg_count = 0; + } + } + + break; + } + } + + if (!arg_count) { + /* completed Op, prepare for next */ + + if (acpi_ps_is_named_op (op->opcode)) { + if (acpi_gbl_depth) { + acpi_gbl_depth--; + } + + if (op->opcode == AML_REGION_OP) { + deferred_op = acpi_ps_to_deferred_op (op); + if (deferred_op) { + /* + * Skip parsing of control method or opregion body, + * because we don't have enough info in the first pass + * to parse them correctly. + * + * Completed parsing an Op_region declaration, we now + * know the length. + */ + + deferred_op->body_length = parser_state->aml - + deferred_op->body; + } + } + + +#ifndef PARSER_ONLY + data_type = acpi_ds_map_named_opcode_to_data_type (op->opcode); + + if (op->opcode == AML_NAME_OP) { + if (op->value.arg) { + data_type = acpi_ds_map_opcode_to_data_type ( + (op->value.arg)->opcode, NULL); + ((ACPI_NAMED_OBJECT*)op->acpi_named_object)->type = + (u8) data_type; + } + } + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope (data_type)) { + + acpi_ds_scope_stack_pop (walk_state); + } +#endif + } + + + parser_state->scope->arg_count--; + + + /* Delete op if asked to */ + +#ifndef PARSER_ONLY + if (parse_flags & PARSE_DELETE_TREE) { + acpi_ps_delete_parse_tree (op); + } +#endif + + + if (acpi_ps_has_completed_scope (parser_state)) { + acpi_ps_pop_scope (parser_state, &op, &arg_types); + } + + else { + op = NULL; + } + } + + else { + /* complex argument, push Op and prepare for argument */ + + acpi_ps_push_scope (parser_state, op, arg_types, arg_count); + op = NULL; + } + + } /* while Parser_state->Aml */ + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_parse_aml + * + * PARAMETERS: Start_scope - The starting point of the parse. Becomes the + * root of the parsed op tree. + * Aml - Pointer to the raw AML code to parse + * Aml_size - Length of the AML to parse + * + * RETURN: Status + * + * DESCRIPTION: Parse raw AML and return a tree of ops + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_parse_aml ( + ACPI_GENERIC_OP *start_scope, + u8 *aml, + u32 aml_size, + u32 parse_flags) +{ + ACPI_STATUS status; + ACPI_PARSE_STATE *parser_state; + ACPI_WALK_STATE *walk_state; + ACPI_WALK_LIST walk_list; + ACPI_NAMED_OBJECT *entry = NULL; + + + /* Initialize parser state and scope */ + + parser_state = acpi_ps_create_state (aml, aml_size); + if (!parser_state) { + return (AE_NO_MEMORY); + } + + acpi_ps_init_scope (parser_state, start_scope); + + + /* Initialize a new walk list */ + + walk_list.walk_state = NULL; + + walk_state = acpi_ds_create_walk_state (TABLE_ID_DSDT, NULL, NULL, &walk_list); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + + + /* Setup the current scope */ + + entry = parser_state->start_op->acpi_named_object; + if (entry) { + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push (entry->child_table, entry->type, + walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + } + + + /* Create the parse tree */ + + status = acpi_ps_parse_loop (parser_state, walk_state, parse_flags); + + +cleanup: + + /* Cleanup */ + + acpi_ds_delete_walk_state (walk_state); + acpi_ps_cleanup_scope (parser_state); + acpi_cm_free (parser_state); + + + return (status); +} + + diff --git a/drivers/acpi/parser/psscope.c b/drivers/acpi/parser/psscope.c new file mode 100644 index 000000000..c47cbf84a --- /dev/null +++ b/drivers/acpi/parser/psscope.c @@ -0,0 +1,271 @@ +/****************************************************************************** + * + * Module Name: psscope - Parser scope stack management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" + +#define _COMPONENT PARSER + MODULE_NAME ("psscope"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_parent_scope + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Pointer to an Op object + * + * DESCRIPTION: Get parent of current op being parsed + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_parent_scope ( + ACPI_PARSE_STATE *parser_state) +{ + return parser_state->scope->op; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_has_completed_scope + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Boolean, TRUE = scope completed. + * + * DESCRIPTION: Is parsing of current argument complete? Determined by + * 1) AML pointer is at or beyond the end of the scope + * 2) The scope argument count has reached zero. + * + ******************************************************************************/ + +u8 +acpi_ps_has_completed_scope ( + ACPI_PARSE_STATE *parser_state) +{ + return (u8) ((parser_state->aml >= parser_state->scope->arg_end || + !parser_state->scope->arg_count)); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_init_scope + * + * PARAMETERS: Parser_state - Current parser state object + * Root - the root object of this new scope + * + * RETURN: Status + * + * DESCRIPTION: Allocate and init a new scope object + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_init_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *root) +{ + ACPI_PARSE_SCOPE *scope; + + + scope = acpi_cm_callocate (sizeof (ACPI_PARSE_SCOPE)); + if (!scope) { + return AE_NO_MEMORY; + } + + scope->op = root; + scope->arg_count = ACPI_VAR_ARGS; + scope->arg_end = parser_state->aml_end; + scope->pkg_end = parser_state->aml_end; + parser_state->scope = scope; + parser_state->start_op = root; + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_push_scope + * + * PARAMETERS: Parser_state - Current parser state object + * Op - Current op to be pushed + * Next_arg - Next op argument (to be pushed) + * Arg_count - Fixed or variable number of args + * + * RETURN: Status + * + * DESCRIPTION: Push current op to begin parsing its argument + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_push_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP *op, + u32 remaining_args, + u32 arg_count) +{ + ACPI_PARSE_SCOPE *scope = parser_state->scope_avail; + + + if (scope) { + /* grabbed scope from available list */ + + parser_state->scope_avail = scope->parent; + } + + else { + /* allocate scope from the heap */ + + scope = (ACPI_PARSE_SCOPE*) acpi_cm_allocate (sizeof (ACPI_PARSE_SCOPE)); + if (!scope) { + return (AE_NO_MEMORY); + } + } + + /* Always zero out the scope before init */ + + MEMSET (scope, 0, sizeof (*scope)); + + scope->op = op; + scope->arg_list = remaining_args; + scope->arg_count = arg_count; + scope->pkg_end = parser_state->pkg_end; + scope->parent = parser_state->scope; + parser_state->scope = scope; + + if (arg_count == ACPI_VAR_ARGS) { + /* multiple arguments */ + + scope->arg_end = parser_state->pkg_end; + } + + else { + /* single argument */ + + scope->arg_end = ACPI_MAX_AML; + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_pop_scope + * + * PARAMETERS: Parser_state - Current parser state object + * Op - Where the popped op is returned + * Next_arg - Where the popped "next argument" is + * returned + * + * RETURN: Status + * + * DESCRIPTION: Return to parsing a previous op + * + ******************************************************************************/ + +void +acpi_ps_pop_scope ( + ACPI_PARSE_STATE *parser_state, + ACPI_GENERIC_OP **op, + u32 *arg_list) +{ + ACPI_PARSE_SCOPE *scope = parser_state->scope; + + + if (scope->parent) { + /* return to parsing previous op */ + + *op = scope->op; + *arg_list = scope->arg_list; + parser_state->pkg_end = scope->pkg_end; + parser_state->scope = scope->parent; + + /* add scope to available list */ + + scope->parent = parser_state->scope_avail; + parser_state->scope_avail = scope; + } + + else { + /* empty parse stack, prepare to fetch next opcode */ + + *op = NULL; + *arg_list = 0; + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_cleanup_scope + * + * PARAMETERS: Parser_state - Current parser state object + * + * RETURN: Status + * + * DESCRIPTION: Destroy available list, remaining stack levels, and return + * root scope + * + ******************************************************************************/ + +void +acpi_ps_cleanup_scope ( + ACPI_PARSE_STATE *parser_state) +{ + ACPI_PARSE_SCOPE *scope; + + + if (!parser_state) { + return; + } + + /* destroy available list */ + + while (parser_state->scope_avail) { + scope = parser_state->scope_avail; + parser_state->scope_avail = scope->parent; + acpi_cm_free (scope); + } + + /* destroy scope stack */ + + while (parser_state->scope) { + scope = parser_state->scope; + parser_state->scope = scope->parent; + acpi_cm_free (scope); + } + + return; +} + diff --git a/drivers/acpi/parser/pstree.c b/drivers/acpi/parser/pstree.c new file mode 100644 index 000000000..377574071 --- /dev/null +++ b/drivers/acpi/parser/pstree.c @@ -0,0 +1,399 @@ +/****************************************************************************** + * + * Module Name: pstree - Parser op tree manipulation/traversal/search + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" + +#define _COMPONENT PARSER + MODULE_NAME ("pstree"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_arg + * + * PARAMETERS: Op - Get an argument for this op + * Argn - Nth argument to get + * + * RETURN: The argument (as an Op object). NULL if argument does not exist + * + * DESCRIPTION: Get the specified op's argument. + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_arg ( + ACPI_GENERIC_OP *op, + u32 argn) +{ + ACPI_GENERIC_OP *arg = NULL; + ACPI_OP_INFO *op_info; + + + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info (op->opcode); + if (!op_info) { + /* Invalid opcode */ + + return NULL; + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & OP_INFO_HAS_ARGS)) { + /* Has no linked argument objects */ + + return NULL; + } + + /* Get the requested argument object */ + + arg = op->value.arg; + while (arg && argn) { + argn--; + arg = arg->next; + } + + return arg; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_append_arg + * + * PARAMETERS: Op - Append an argument to this Op. + * Arg - Argument Op to append + * + * RETURN: None. + * + * DESCRIPTION: Append an argument to an op's argument list (a NULL arg is OK) + * + ******************************************************************************/ + +void +acpi_ps_append_arg ( + ACPI_GENERIC_OP *op, + ACPI_GENERIC_OP *arg) +{ + ACPI_GENERIC_OP *prev_arg; + ACPI_OP_INFO *op_info; + + + if (!op) { + return; + } + + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info (op->opcode); + if (!op_info) { + /* Invalid opcode */ + + return; + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & OP_INFO_HAS_ARGS)) { + /* Has no linked argument objects */ + + return; + } + + + /* Append the argument to the linked argument list */ + + if (op->value.arg) { + /* Append to existing argument list */ + + prev_arg = op->value.arg; + while (prev_arg->next) { + prev_arg = prev_arg->next; + } + prev_arg->next = arg; + } + + else { + /* No argument list, this will be the first argument */ + + op->value.arg = arg; + } + + + /* Set the parent in this arg and any args linked after it */ + + while (arg) { + arg->parent = op; + arg = arg->next; + } +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_child + * + * PARAMETERS: Op - Get the child of this Op + * + * RETURN: Child Op, Null if none is found. + * + * DESCRIPTION: Get op's children or NULL if none + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_child ( + ACPI_GENERIC_OP *op) +{ + ACPI_GENERIC_OP *child = NULL; + + + switch (op->opcode) + { + case AML_SCOPE_OP: + case AML_ELSE_OP: + case AML_DEVICE_OP: + case AML_THERMAL_ZONE_OP: + case AML_METHODCALL_OP: + + child = acpi_ps_get_arg (op, 0); + break; + + + case AML_BUFFER_OP: + case AML_PACKAGE_OP: + case AML_METHOD_OP: + case AML_IF_OP: + case AML_WHILE_OP: + case AML_DEF_FIELD_OP: + + child = acpi_ps_get_arg (op, 1); + break; + + + case AML_POWER_RES_OP: + case AML_INDEX_FIELD_OP: + + child = acpi_ps_get_arg (op, 2); + break; + + + case AML_PROCESSOR_OP: + case AML_BANK_FIELD_OP: + + child = acpi_ps_get_arg (op, 3); + break; + + } + + return child; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_depth_next + * + * PARAMETERS: Origin - Root of subtree to search + * Op - Last (previous) Op that was found + * + * RETURN: Next Op found in the search. + * + * DESCRIPTION: Get next op in tree (walking the tree in depth-first order) + * Return NULL when reaching "origin" or when walking up from root + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_get_depth_next ( + ACPI_GENERIC_OP *origin, + ACPI_GENERIC_OP *op) +{ + ACPI_GENERIC_OP *next = NULL; + ACPI_GENERIC_OP *parent; + ACPI_GENERIC_OP *arg; + + + if (!op) { + return NULL; + } + + /* look for an argument or child */ + + next = acpi_ps_get_arg (op, 0); + if (next) { + return next; + } + + /* look for a sibling */ + + next = op->next; + if (next) { + return next; + } + + /* look for a sibling of parent */ + + parent = op->parent; + + while (parent) { + arg = acpi_ps_get_arg (parent, 0); + while (arg && (arg != origin) && (arg != op)) { + arg = arg->next; + } + + if (arg == origin) { + /* reached parent of origin, end search */ + + return NULL; + } + + if (parent->next) { + /* found sibling of parent */ + return parent->next; + } + + op = parent; + parent = parent->parent; + } + + return next; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_fetch_prefix + * + * PARAMETERS: Scope - Op to fetch prefix for + * Path - A namestring containing the prefix + * io - Direction flag + * + * RETURN: Op referenced by the prefix + * + * DESCRIPTION: Fetch and handle path prefix ('\\' or '^') + * + ******************************************************************************/ + +ACPI_GENERIC_OP * +acpi_ps_fetch_prefix ( + ACPI_GENERIC_OP *scope, + char **path, + u32 io) +{ + u32 prefix = io ? GET8 (*path):**path; + + + switch (prefix) + { + case '\\': + case '/': + + /* go to the root */ + + *path += 1; + while (scope->parent) { + scope = scope->parent; + } + break; + + + case '^': + + /* go up one level */ + + *path += 1; + scope = scope->parent; + break; + } + + if (scope && !scope->parent) { + /* searching from the root, start with its children */ + + scope = acpi_ps_get_child (scope); + } + + return scope; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_fetch_name + * + * PARAMETERS: Path - A string containing the name segment + * io - Direction flag + * + * RETURN: The 4-char ASCII ACPI Name as a u32 + * + * DESCRIPTION: Fetch ACPI name segment (dot-delimited) + * + ******************************************************************************/ + +u32 +acpi_ps_fetch_name ( + char **path, + u32 io) +{ + u32 name = 0; + char *nm; + u32 i; + char ch; + + + if (io) { + /* Get the name from the path pointer */ + + MOVE_UNALIGNED32_TO_32 (&name, *path); + *path += 4; + } + + else { + if (**path == '.') { + *path += 1; + } + + nm = (char*) &name; + for (i = 0; i < 4; i++) { + ch = **path; + if (ch && ch != '.') { + *nm = ch; + *path += 1; + } + + else { + *nm = '_'; + } + nm++; + } + } + + return name; +} + + diff --git a/drivers/acpi/parser/psutils.c b/drivers/acpi/parser/psutils.c new file mode 100644 index 000000000..dff77c52d --- /dev/null +++ b/drivers/acpi/parser/psutils.c @@ -0,0 +1,498 @@ +/****************************************************************************** + * + * Module Name: psutils - Parser miscellaneous utilities (Parser only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "amlcode.h" + +#define _COMPONENT PARSER + MODULE_NAME ("psutils"); + + +#define PARSEOP_GENERIC 1 +#define PARSEOP_NAMED 2 +#define PARSEOP_DEFERRED 3 +#define PARSEOP_BYTELIST 4 + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_init_op + * + * PARAMETERS: Op - A newly allocated Op object + * Opcode - Opcode to store in the Op + * + * RETURN: Status + * + * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on + * opcode + * + ******************************************************************************/ + +void +acpi_ps_init_op ( + ACPI_GENERIC_OP *op, + u16 opcode) +{ + ACPI_OP_INFO *aml_op; + + + op->data_type = ACPI_DESC_TYPE_PARSER; + op->opcode = opcode; + + + aml_op = acpi_ps_get_opcode_info (opcode); + if (aml_op) { + DEBUG_ONLY_MEMBERS (STRNCPY (op->op_name, aml_op->name, + sizeof (op->op_name))); + } +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_alloc_op + * + * PARAMETERS: Opcode - Opcode that will be stored in the new Op + * + * RETURN: Pointer to the new Op. + * + * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on + * opcode. A cache of opcodes is available for the pure + * GENERIC_OP, since this is by far the most commonly used. + * + ******************************************************************************/ + +ACPI_GENERIC_OP* +acpi_ps_alloc_op ( + u16 opcode) +{ + ACPI_GENERIC_OP *op = NULL; + u32 size; + u8 flags; + + + /* Allocate the minimum required size object */ + + if (acpi_ps_is_deferred_op (opcode)) { + size = sizeof (ACPI_DEFERRED_OP); + flags = PARSEOP_DEFERRED; + } + + else if (acpi_ps_is_named_op (opcode)) { + size = sizeof (ACPI_NAMED_OP); + flags = PARSEOP_NAMED; + } + + else if (acpi_ps_is_bytelist_op (opcode)) { + size = sizeof (ACPI_BYTELIST_OP); + flags = PARSEOP_BYTELIST; + } + + else { + size = sizeof (ACPI_GENERIC_OP); + flags = PARSEOP_GENERIC; + + /* + * The generic op is by far the most common (16 to 1), and therefore + * the op cache is implemented with this type. + * + * Check if there is an Op already available in the cache + */ + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + acpi_gbl_parse_cache_requests++; + if (acpi_gbl_parse_cache) { + /* Extract an op from the front of the cache list */ + + acpi_gbl_parse_cache_depth--; + acpi_gbl_parse_cache_hits++; + + op = acpi_gbl_parse_cache; + acpi_gbl_parse_cache = op->next; + + /* Clear the previously used Op */ + + MEMSET (op, 0, sizeof (ACPI_GENERIC_OP)); + } + acpi_cm_release_mutex (ACPI_MTX_CACHES); + } + + /* Allocate a new Op if necessary */ + + if (!op) { + op = acpi_cm_callocate (size); + } + + /* Initialize the Op */ + if (op) { + acpi_ps_init_op (op, opcode); + op->flags = flags; + } + + return op; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_free_op + * + * PARAMETERS: Op - Op to be freed + * + * RETURN: None. + * + * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list + * or actually free it. + * + ******************************************************************************/ + +void +acpi_ps_free_op ( + ACPI_GENERIC_OP *op) +{ + + + if (op->flags == PARSEOP_GENERIC) { + /* Is the cache full? */ + + if (acpi_gbl_parse_cache_depth < MAX_PARSE_CACHE_DEPTH) { + /* Put a GENERIC_OP back into the cache */ + + acpi_cm_acquire_mutex (ACPI_MTX_CACHES); + acpi_gbl_parse_cache_depth++; + + op->next = acpi_gbl_parse_cache; + acpi_gbl_parse_cache = op; + + acpi_cm_release_mutex (ACPI_MTX_CACHES); + return; + } + } + + /* + * Not a GENERIC OP, or the cache is full, just free the Op + */ + + acpi_cm_free (op); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_delete_parse_cache + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Free all objects that are on the parse cache list. + * + ******************************************************************************/ + +void +acpi_ps_delete_parse_cache ( + void) +{ + ACPI_GENERIC_OP *next; + + + /* Traverse the global cache list */ + + while (acpi_gbl_parse_cache) { + /* Delete one cached state object */ + + next = acpi_gbl_parse_cache->next; + acpi_cm_free (acpi_gbl_parse_cache); + acpi_gbl_parse_cache = next; + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Utility functions + * + * DESCRIPTION: Low level functions + * + * TBD: [Restructure] + * 1) Some of these functions should be macros + * 2) Some can be simplified + * + ******************************************************************************/ + + +/* + * Is "c" a namestring lead character? + */ + + +u8 +acpi_ps_is_leading_char ( + s32 c) +{ + return ((u8) (c == '_' || (c >= 'A' && c <= 'Z'))); +} + + +/* + * Is "c" a namestring prefix character? + */ +u8 +acpi_ps_is_prefix_char ( + s32 c) +{ + return ((u8) (c == '\\' || c == '^')); +} + + +u8 +acpi_ps_is_namespace_object_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_SCOPE_OP || + opcode == AML_DEVICE_OP || + opcode == AML_THERMAL_ZONE_OP || + opcode == AML_METHOD_OP || + opcode == AML_POWER_RES_OP || + opcode == AML_PROCESSOR_OP || + opcode == AML_DEF_FIELD_OP || + opcode == AML_INDEX_FIELD_OP || + opcode == AML_BANK_FIELD_OP || + opcode == AML_NAMEDFIELD_OP || + opcode == AML_NAME_OP || + opcode == AML_ALIAS_OP || + opcode == AML_MUTEX_OP || + opcode == AML_EVENT_OP || + opcode == AML_REGION_OP || + opcode == AML_CREATE_FIELD_OP || + opcode == AML_BIT_FIELD_OP || + opcode == AML_BYTE_FIELD_OP || + opcode == AML_WORD_FIELD_OP || + opcode == AML_DWORD_FIELD_OP || + opcode == AML_METHODCALL_OP || + opcode == AML_NAMEPATH_OP)); +} + +u8 +acpi_ps_is_namespace_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_SCOPE_OP || + opcode == AML_DEVICE_OP || + opcode == AML_THERMAL_ZONE_OP || + opcode == AML_METHOD_OP || + opcode == AML_POWER_RES_OP || + opcode == AML_PROCESSOR_OP || + opcode == AML_DEF_FIELD_OP || + opcode == AML_INDEX_FIELD_OP || + opcode == AML_BANK_FIELD_OP || + opcode == AML_NAME_OP || + opcode == AML_ALIAS_OP || + opcode == AML_MUTEX_OP || + opcode == AML_EVENT_OP || + opcode == AML_REGION_OP || + opcode == AML_NAMEDFIELD_OP)); +} + + +/* + * Is opcode for a named object Op? + * (Includes all named object opcodes) + * + * TBD: [Restructure] Need a better way than this brute force approach! + */ +u8 +acpi_ps_is_named_object_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_SCOPE_OP || + opcode == AML_DEVICE_OP || + opcode == AML_THERMAL_ZONE_OP || + opcode == AML_METHOD_OP || + opcode == AML_POWER_RES_OP || + opcode == AML_PROCESSOR_OP || + opcode == AML_NAMEDFIELD_OP || + opcode == AML_NAME_OP || + opcode == AML_ALIAS_OP || + opcode == AML_MUTEX_OP || + opcode == AML_EVENT_OP || + opcode == AML_REGION_OP || + + + opcode == AML_CREATE_FIELD_OP || + opcode == AML_BIT_FIELD_OP || + opcode == AML_BYTE_FIELD_OP || + opcode == AML_WORD_FIELD_OP || + opcode == AML_DWORD_FIELD_OP || + opcode == AML_METHODCALL_OP || + opcode == AML_NAMEPATH_OP)); +} + + +/* + * Is opcode for a named Op? + */ +u8 +acpi_ps_is_named_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_SCOPE_OP || + opcode == AML_DEVICE_OP || + opcode == AML_THERMAL_ZONE_OP || + opcode == AML_METHOD_OP || + opcode == AML_POWER_RES_OP || + opcode == AML_PROCESSOR_OP || + opcode == AML_NAME_OP || + opcode == AML_ALIAS_OP || + opcode == AML_MUTEX_OP || + opcode == AML_EVENT_OP || + opcode == AML_REGION_OP || + opcode == AML_NAMEDFIELD_OP)); +} + + +u8 +acpi_ps_is_deferred_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_METHOD_OP || + opcode == AML_REGION_OP)); +} + + +/* + * Is opcode for a bytelist? + */ +u8 +acpi_ps_is_bytelist_op ( + u16 opcode) +{ + return ((u8) (opcode == AML_BYTELIST_OP)); +} + + +/* + * Is opcode for a Field, Index_field, or Bank_field + */ +u8 +acpi_ps_is_field_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_CREATE_FIELD_OP + || opcode == AML_DEF_FIELD_OP + || opcode == AML_INDEX_FIELD_OP + || opcode == AML_BANK_FIELD_OP)); +} + + +/* + * Is field creation op + */ +u8 +acpi_ps_is_create_field_op ( + u16 opcode) +{ + return ((u8) + (opcode == AML_CREATE_FIELD_OP || + opcode == AML_BIT_FIELD_OP || + opcode == AML_BYTE_FIELD_OP || + opcode == AML_WORD_FIELD_OP || + opcode == AML_DWORD_FIELD_OP)); +} + + +/* + * Cast an acpi_op to an acpi_deferred_op if possible + */ +ACPI_DEFERRED_OP * +acpi_ps_to_deferred_op ( + ACPI_GENERIC_OP *op) +{ + return (acpi_ps_is_deferred_op (op->opcode) + ? ( (ACPI_DEFERRED_OP *) op) : NULL); +} + + +/* + * Cast an acpi_op to an acpi_named_op if possible + */ +ACPI_NAMED_OP* +acpi_ps_to_named_op ( + ACPI_GENERIC_OP *op) +{ + return (acpi_ps_is_named_op (op->opcode) + ? ( (ACPI_NAMED_OP *) op) : NULL); +} + + +/* + * Cast an acpi_op to an acpi_bytelist_op if possible + */ +ACPI_BYTELIST_OP* +acpi_ps_to_bytelist_op ( + ACPI_GENERIC_OP *op) +{ + return (acpi_ps_is_bytelist_op (op->opcode) + ? ( (ACPI_BYTELIST_OP*) op) : NULL); +} + + +/* + * Get op's name (4-byte name segment) or 0 if unnamed + */ +u32 +acpi_ps_get_name ( + ACPI_GENERIC_OP *op) +{ + ACPI_NAMED_OP *named = acpi_ps_to_named_op (op); + + return (named ? named->name : 0); +} + + +/* + * Set op's name + */ +void +acpi_ps_set_name ( + ACPI_GENERIC_OP *op, + u32 name) +{ + ACPI_NAMED_OP *named = acpi_ps_to_named_op (op); + + if (named) { + named->name = name; + } +} + diff --git a/drivers/acpi/parser/pswalk.c b/drivers/acpi/parser/pswalk.c new file mode 100644 index 000000000..da83c33b0 --- /dev/null +++ b/drivers/acpi/parser/pswalk.c @@ -0,0 +1,581 @@ +/****************************************************************************** + * + * Module Name: pswalk - Parser routines to walk parsed op tree(s) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "amlcode.h" +#include "parser.h" +#include "dispatch.h" +#include "namesp.h" +#include "interp.h" + +#define _COMPONENT PARSER + MODULE_NAME ("pswalk"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_get_next_walk_op + * + * PARAMETERS: Walk_state - Current state of the walk + * Op - Current Op to be walked + * Ascending_callback - Procedure called when Op is complete + * Prev_op - Where the previous Op is stored + * Next_op - Where the next Op in the walk is stored + * + * RETURN: Status + * + * DESCRIPTION: Get the next Op in a walk of the parse tree. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_get_next_walk_op ( + ACPI_WALK_STATE *walk_state, + ACPI_GENERIC_OP *op, + INTERPRETER_CALLBACK ascending_callback) +{ + ACPI_GENERIC_OP *next; + ACPI_GENERIC_OP *parent; + ACPI_GENERIC_OP *grand_parent; + ACPI_STATUS status; + + + /* Check for a argument only if we are descending in the tree */ + + if (walk_state->next_op_info != NEXT_OP_UPWARD) { + /* Look for an argument or child of the current op */ + + next = acpi_ps_get_arg (op, 0); + if (next) { + /* Still going downward in tree (Op is not completed yet) */ + + walk_state->prev_op = op; + walk_state->next_op = next; + walk_state->next_op_info = NEXT_OP_DOWNWARD; + + return (AE_OK); + } + + + /* + * No more children, this Op is complete. Save Next and Parent + * in case the Op object gets deleted by the callback routine + */ + + next = op->next; + parent = op->parent; + + status = ascending_callback (walk_state, op); + + switch (status) + { + case AE_CTRL_TERMINATE: + + /* + * A control method was terminated via a RETURN statement. + * The walk of this method is complete. + */ + walk_state->prev_op = walk_state->origin; + walk_state->next_op = NULL; + + return (AE_OK); + break; + + + case AE_CTRL_FALSE: + + /* + * Either an IF/WHILE Predicate was false or we encountered a BREAK + * opcode. In both cases, we do not execute the rest of the + * package; We simply close out the parent (finishing the walk of + * this branch of the tree) and continue execution at the parent + * level. + */ + + next = parent->next; + status = AE_OK; + + /* + * If there is a sibling to the parent, we must close out the + * parent now, because we are going to continue to go downward (to + * the sibling) in the parse tree. + */ + if (next) { + status = ascending_callback (walk_state, parent); + + /* The parent sibling will be next */ + + walk_state->prev_op = op; + walk_state->next_op = next; + walk_state->next_op_info = NEXT_OP_DOWNWARD; + + /* Continue downward */ + + return (AE_OK); + } + + /* + * Drop into the loop below because we are moving upwards in + * the tree + */ + + break; + + + default: + /* + * Check for a sibling to the current op. A sibling means + * we are still going "downward" in the tree. + */ + + if (next) { + /* There is a sibling, it will be next */ + + walk_state->prev_op = op; + walk_state->next_op = next; + walk_state->next_op_info = NEXT_OP_DOWNWARD; + + /* Continue downward */ + + return (status); + } + + /* + * No sibling, but check status. + * Abort on error from callback routine + */ + if (status != AE_OK) { + /* Next op will be the parent */ + + walk_state->prev_op = op; + walk_state->next_op = parent; + walk_state->next_op_info = NEXT_OP_UPWARD; + + return (status); + } + + /* + * Drop into the loop below because we are moving upwards in + * the tree + */ + + break; + } + } + + else { + /* + * We are resuming a walk, and we were (are) going upward in the tree. + * So, we want to drop into the parent loop below. + */ + + parent = op; + } + + + /* + * Look for a sibling of the current Op's parent + * Continue moving up the tree until we find a node that has not been + * visited, or we get back to where we started. + */ + while (parent) { + /* We are moving up the tree, therefore this parent Op is complete */ + + grand_parent = parent->parent; + next = parent->next; + + status = ascending_callback (walk_state, parent); + + + switch (status) + { + case AE_CTRL_FALSE: + + /* + * Either an IF/WHILE Predicate was false or we encountered a + * BREAK opcode. In both cases, we do not execute the rest of the + * package; We simply close out the parent (finishing the walk of + * this branch of the tree) and continue execution at the parent + * level. + */ + + parent = grand_parent; + next = grand_parent->next; + grand_parent = grand_parent->parent; + + status = ascending_callback (walk_state, parent); + + /* Now continue to the next node in the tree */ + + break; + + + case AE_CTRL_TRUE: + + /* + * Predicate of a WHILE was true and the loop just completed an + * execution. Go back to the start of the loop and reevaluate the + * predicate. + */ + + op = walk_state->control_state->control.predicate_op; + + walk_state->control_state->common.state = CONTROL_PREDICATE_EXECUTING; + + /* + * Acpi_evaluate the predicate again (next) + * Because we will traverse WHILE tree again + */ + + walk_state->prev_op = op->parent; + walk_state->next_op = op; + walk_state->next_op_info = NEXT_OP_DOWNWARD; + + return (AE_OK); + break; + + + case AE_CTRL_TERMINATE: + + /* + * A control method was terminated via a RETURN statement. + * The walk of this method is complete. + */ + walk_state->prev_op = walk_state->origin; + walk_state->next_op = NULL; + + return (AE_OK); + break; + } + + + /* + * If we are back to the starting point, the walk is complete. + */ + if (parent == walk_state->origin) { + /* Reached the point of origin, the walk is complete */ + + walk_state->prev_op = parent; + walk_state->next_op = NULL; + + return (status); + } + + + /* + * If there is a sibling to this parent (it is not the starting point + * Op), then we will visit it. + */ + if (next) { + /* found sibling of parent */ + + walk_state->prev_op = parent; + walk_state->next_op = next; + walk_state->next_op_info = NEXT_OP_DOWNWARD; + + return (status); + } + + /* + * No sibling, check for an error from closing the parent + * (Also, AE_PENDING if a method call was encountered) + */ + if (status != AE_OK) { + walk_state->prev_op = parent; + walk_state->next_op = grand_parent; + walk_state->next_op_info = NEXT_OP_UPWARD; + + return (status); + } + + /* No siblings, no errors, just move up one more level in the tree */ + + op = parent; + parent = grand_parent; + walk_state->prev_op = op; + } + + + /* Got all the way to the top of the tree, we must be done! */ + /* However, the code should have terminated in the loop above */ + + walk_state->next_op = NULL; + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_walk_loop + * + * PARAMETERS: Walk_list - State of the walk + * Start_op - Starting Op of the subtree to be walked + * Descending_callback - Procedure called when a new Op is + * encountered + * Ascending_callback - Procedure called when Op is complete + * + * RETURN: Status + * + * DESCRIPTION: Perform a walk of the parsed AML tree. Begins and terminates at + * the Start_op. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_walk_loop ( + ACPI_WALK_LIST *walk_list, + ACPI_GENERIC_OP *start_op, + INTERPRETER_CALLBACK descending_callback, + INTERPRETER_CALLBACK ascending_callback) +{ + ACPI_STATUS status = AE_OK; + ACPI_WALK_STATE *walk_state; + ACPI_GENERIC_OP *op = start_op; + + + walk_state = acpi_ds_get_current_walk_state (walk_list); + + + /* Walk entire subtree, visiting all nodes depth-first */ + + while (op) { + if (walk_state->next_op_info != NEXT_OP_UPWARD) { + status = descending_callback (walk_state, op); + } + + /* + * A TRUE exception means that an ELSE was detected, but the IF + * predicate evaluated TRUE. + */ + if (status == AE_CTRL_TRUE) { + /* + * Ignore the entire ELSE block by moving on to the the next opcode. + * And we do that by simply going up in the tree (either to the next + * sibling or to the parent) from here. + */ + + walk_state->next_op_info = NEXT_OP_UPWARD; + } + + /* Get the next node (op) in the depth-first walk */ + + status = acpi_ps_get_next_walk_op (walk_state, op, ascending_callback); + + /* + * A PENDING exception means that a control method invocation has been + * detected + */ + + if (status == AE_CTRL_PENDING) { + /* Transfer control to the called control method */ + + status = acpi_ds_call_control_method (walk_list, walk_state, op); + + /* + * If the transfer to the new method method call worked, a new walk + * state was created -- get it + */ + + walk_state = acpi_ds_get_current_walk_state (walk_list); + } + + /* Abort the walk on any exception */ + + if (ACPI_FAILURE (status)) { + return (status); + } + + op = walk_state->next_op; + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_ps_walk_parsed_aml + * + * PARAMETERS: Start_op - Starting Op of the subtree to be walked + * End_op - Where to terminate the walk + * Descending_callback - Procedure called when a new Op is + * encountered + * Ascending_callback - Procedure called when Op is complete + * + * RETURN: Status + * + * DESCRIPTION: Top level interface to walk the parsed AML tree. Handles + * preemption of executing control methods. + * + * NOTE: The End_op is usually only different from the Start_op if + * we don't want to visit the Start_op during the tree descent. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_ps_walk_parsed_aml ( + ACPI_GENERIC_OP *start_op, + ACPI_GENERIC_OP *end_op, + ACPI_OBJECT_INTERNAL *mth_desc, + ACPI_NAME_TABLE *start_scope, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **caller_return_desc, + ACPI_OWNER_ID owner_id, + INTERPRETER_CALLBACK descending_callback, + INTERPRETER_CALLBACK ascending_callback) +{ + ACPI_GENERIC_OP *op; + ACPI_WALK_STATE *walk_state; + ACPI_OBJECT_INTERNAL *return_desc; + ACPI_STATUS status; + ACPI_WALK_LIST walk_list; + ACPI_WALK_LIST *prev_walk_list; + + + /* Parameter Validation */ + + if (!start_op || !end_op) { + return AE_BAD_PARAMETER; + } + + /* Initialize a new walk list */ + + walk_list.walk_state = NULL; + + walk_state = acpi_ds_create_walk_state (owner_id, end_op, mth_desc, &walk_list); + if (!walk_state) { + return (AE_NO_MEMORY); + } + + /* TBD: [Restructure] TEMP until we pass Walk_state to the interpreter + */ + prev_walk_list = acpi_gbl_current_walk_list; + acpi_gbl_current_walk_list = &walk_list; + + if (start_scope) { + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push (start_scope, ACPI_TYPE_METHOD, walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + } + + if (mth_desc) { + /* Init arguments if this is a control method */ + /* TBD: [Restructure] add walkstate as a param */ + + acpi_ds_method_data_init_args (params, MTH_NUM_ARGS); + } + + op = start_op; + status = AE_OK; + + + /* + * Execute the walk loop as long as there is a valid Walk State. This + * handles nested control method invocations without recursion. + */ + + while (walk_state) { + if (status == AE_OK) { + status = acpi_ps_walk_loop (&walk_list, op, descending_callback, + ascending_callback); + } + + /* We are done with this walk, move on to the parent if any */ + + BREAKPOINT3; + + walk_state = acpi_ds_pop_walk_state (&walk_list); + + /* Extract return value before we delete Walk_state */ + + return_desc = walk_state->return_desc; + + /* Reset the current scope to the beginning of scope stack */ + + acpi_ds_scope_stack_clear (walk_state); + + /* + * If we just returned from the execution of a control method, + * there's lots of cleanup to do + */ + + if (walk_state->method_desc && + walk_state->method_desc->method.parser_op) + { + acpi_ds_terminate_control_method (walk_state); + } + + /* Delete this walk state and all linked control states */ + + acpi_ds_delete_walk_state (walk_state); + + /* Check if we have restarted a preempted walk */ + + walk_state = acpi_ds_get_current_walk_state (&walk_list); + if (walk_state && + status == AE_OK) + { + /* There is another walk state, restart it */ + + /* + * If the method returned value is not used by the parent, + * The object is deleted + */ + + acpi_ds_restart_control_method (walk_state, return_desc); + + /* Get the next Op to process */ + + op = walk_state->next_op; + } + + /* + * Just completed a 1st-level method, save the final internal return + * value (if any) + */ + + else if (caller_return_desc) { + *caller_return_desc = return_desc; /* NULL if no return value */ + } + + else if (return_desc) { + /* Caller doesn't want it, must delete it */ + + acpi_cm_remove_reference (return_desc); + } + } + + + acpi_gbl_current_walk_list = prev_walk_list; + + return (status); +} + + diff --git a/drivers/acpi/parser/psxface.c b/drivers/acpi/parser/psxface.c new file mode 100644 index 000000000..667df681e --- /dev/null +++ b/drivers/acpi/parser/psxface.c @@ -0,0 +1,132 @@ + +/****************************************************************************** + * + * Module Name: psxface - Parser external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "parser.h" +#include "dispatch.h" +#include "interp.h" +#include "amlcode.h" +#include "namesp.h" + + +#define _COMPONENT PARSER + MODULE_NAME ("psxface"); + + +char *acpi_gbl_parser_id = "Non-recursive AML Parser"; + + +/***************************************************************************** + * + * FUNCTION: Acpi_psx_execute + * + * PARAMETERS: Obj_desc - A method object containing both the AML + * address and length. + * **Params - List of parameters to pass to method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method + * + ****************************************************************************/ + +ACPI_STATUS +acpi_psx_execute ( + ACPI_NAMED_OBJECT *method_entry, + ACPI_OBJECT_INTERNAL **params, + ACPI_OBJECT_INTERNAL **return_obj_desc) +{ + ACPI_STATUS status; + ACPI_OBJECT_INTERNAL *obj_desc; + u32 i; + + + /* Validate the NTE and get the attached object */ + + if (!method_entry) { + return (AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object (method_entry); + if (!obj_desc) { + return (AE_NULL_OBJECT); + } + + /* Parse method if necessary, wait on concurrency semaphore */ + + status = acpi_ds_begin_method_execution (method_entry, obj_desc); + if (ACPI_FAILURE (status)) { + return (status); + } + + if (params) { + /* + * The caller "owns" the parameters, so give each one an extra + * reference + */ + + for (i = 0; params[i]; i++) { + acpi_cm_add_reference (params[i]); + } + } + + /* + * Method is parsed and ready to execute + * The walk of the parse tree is where we actually execute the method + */ + + status = acpi_ps_walk_parsed_aml (obj_desc->method.parser_op, + obj_desc->method.parser_op, obj_desc, + method_entry->child_table, params, return_obj_desc, + obj_desc->method.owning_id, acpi_ds_exec_begin_op, + acpi_ds_exec_end_op); + + if (params) { + /* Take away the extra reference that we gave the parameters above */ + + for (i = 0; params[i]; i++) { + acpi_cm_update_object_reference (params[i], REF_DECREMENT); + } + } + + + /* + * Normal exit is with Status == AE_RETURN_VALUE when a Return_op has been + * executed, or with Status == AE_PENDING at end of AML block (end of + * Method code) + */ + + if (*return_obj_desc) { + status = AE_CTRL_RETURN_VALUE; + } + + + return (status); +} + + diff --git a/drivers/acpi/resources/rsaddr.c b/drivers/acpi/resources/rsaddr.c new file mode 100644 index 000000000..70c8001aa --- /dev/null +++ b/drivers/acpi/resources/rsaddr.c @@ -0,0 +1,810 @@ +/****************************************************************************** + * + * Module Name: rsaddr - Acpi_rs_address16_resource + * Acpi_rs_address16_stream + * Acpi_rs_address32_resource + * Acpi_rs_address32_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsaddr"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_address16_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_address16_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 index; + u32 struct_size = sizeof(ADDRESS16_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + + temp16 = *(u16 *)buffer; + + *bytes_consumed = temp16 + 3; + + output_struct->id = address16; + + output_struct->length = struct_size; + + /* + * Get the Resource Type (Byte3) + */ + buffer += 2; + temp8 = *buffer; + + /* Values 0-2 are valid */ + if (temp8 > 2) { + return (AE_ERROR); + } + + output_struct->data.address16.resource_type = temp8 & 0x03; + + /* + * Get the General Flags (Byte4) + */ + buffer += 1; + temp8 = *buffer; + + /* + * Producer / Consumer + */ + output_struct->data.address16.producer_consumer = temp8 & 0x01; + + /* + * Decode + */ + output_struct->data.address16.decode = (temp8 >> 1) & 0x01; + + /* + * Min Address Fixed + */ + output_struct->data.address16.min_address_fixed = (temp8 >> 2) & 0x01; + + /* + * Max Address Fixed + */ + output_struct->data.address16.max_address_fixed = (temp8 >> 3) & 0x01; + + /* + * Get the Type Specific Flags (Byte5) + */ + buffer += 1; + temp8 = *buffer; + + if (MEMORY_RANGE == output_struct->data.address16.resource_type) { + output_struct->data.address16.attribute.memory.read_write_attribute = + (u16) (temp8 & 0x01); + output_struct->data.address16.attribute.memory.cache_attribute = + (u16) ((temp8 >> 1) & 0x0F); + } + + else { + if (IO_RANGE == output_struct->data.address16.resource_type) { + output_struct->data.address16.attribute.io.range_attribute = + (u16) (temp8 & 0x03); + } + + else { + /* BUS_NUMBER_RANGE == Address32_data->Resource_type */ + /* Nothing needs to be filled in */ + } + } + + /* + * Get Granularity (Bytes 6-7) + */ + buffer += 1; + MOVE_UNALIGNED16_TO_16 (&output_struct->data.address16.granularity, + buffer); + + /* + * Get Min_address_range (Bytes 8-9) + */ + buffer += 2; + MOVE_UNALIGNED16_TO_16 (&output_struct->data.address16.min_address_range, + buffer); + + /* + * Get Max_address_range (Bytes 10-11) + */ + buffer += 2; + MOVE_UNALIGNED16_TO_16 + (&output_struct->data.address16.max_address_range, + buffer); + + /* + * Get Address_translation_offset (Bytes 12-13) + */ + buffer += 2; + MOVE_UNALIGNED16_TO_16 + (&output_struct->data.address16.address_translation_offset, + buffer); + + /* + * Get Address_length (Bytes 14-15) + */ + buffer += 2; + MOVE_UNALIGNED16_TO_16 + (&output_struct->data.address16.address_length, + buffer); + + /* + * Resource Source Index (if present) + */ + buffer += 2; + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + */ + if (*bytes_consumed > 16) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.address16.resource_source_index = + (u32) temp8; + + /* Point to the String */ + + buffer += 1; + + /* Copy the string into the buffer */ + + index = 0; + + while (0x00 != *buffer) { + output_struct->data.address16.resource_source[index] = + *buffer; + + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + output_struct->data.address16.resource_source[index] = 0x00; + + output_struct->data.address16.resource_source_string_length = + index + 1; + + /* + * In order for the Struct_size to fall on a 32-bit boundry, + * calculate the length of the string and expand the + * Struct_size to the next 32-bit boundry. + */ + temp8 = (u8) (index + 1); + struct_size += ROUND_UP_TO_32_bITS (temp8); + } + else { + output_struct->data.address16.resource_source_index = 0x00; + output_struct->data.address16.resource_source_string_length = 0; + output_struct->data.address16.resource_source[0] = 0x00; + } + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_address16_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_address16_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 *length_field; + u8 temp8 = 0; + u8 *temp_pointer = NULL; + u32 actual_bytes; + + + /* + * The descriptor field is static + */ + *buffer = 0x88; + buffer += 1; + + /* + * Save a pointer to the Length field - to be filled in later + */ + length_field = buffer; + buffer += 2; + + /* + * Set the Resource Type (Memory, Io, Bus_number) + */ + temp8 = (u8) (linked_list->data.address16.resource_type & 0x03); + *buffer = temp8; + buffer += 1; + + /* + * Set the general flags + */ + temp8 = (u8) (linked_list->data.address16.producer_consumer & 0x01); + + temp8 |= (linked_list->data.address16.decode & 0x01) << 1; + temp8 |= (linked_list->data.address16.min_address_fixed & 0x01) << 2; + temp8 |= (linked_list->data.address16.max_address_fixed & 0x01) << 3; + + *buffer = temp8; + buffer += 1; + + /* + * Set the type specific flags + */ + temp8 = 0; + + if (MEMORY_RANGE == linked_list->data.address16.resource_type) { + temp8 = (u8) + (linked_list->data.address16.attribute.memory.read_write_attribute & + 0x01); + + temp8 |= + (linked_list->data.address16.attribute.memory.cache_attribute & + 0x0F) << 1; + } + + else if (IO_RANGE == linked_list->data.address16.resource_type) { + temp8 = (u8) + (linked_list->data.address16.attribute.io.range_attribute & + 0x03); + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the address space granularity + */ + MOVE_UNALIGNED16_TO_16 (buffer, + &linked_list->data.address16.granularity); + buffer += 2; + + /* + * Set the address range minimum + */ + MOVE_UNALIGNED16_TO_16 (buffer, + &linked_list->data.address16.min_address_range); + + buffer += 2; + + /* + * Set the address range maximum + */ + MOVE_UNALIGNED16_TO_16 (buffer, + &linked_list->data.address16.max_address_range); + + buffer += 2; + + /* + * Set the address translation offset + */ + MOVE_UNALIGNED16_TO_16 (buffer, + &linked_list->data.address16.address_translation_offset); + + buffer += 2; + + /* + * Set the address length + */ + MOVE_UNALIGNED16_TO_16 (buffer, + &linked_list->data.address16.address_length); + + buffer += 2; + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.address16.resource_source_string_length) { + temp8 = (u8) linked_list->data.address16.resource_source_index; + + *buffer = temp8; + buffer += 1; + + temp_pointer = buffer; + + /* + * Copy the string + */ + STRCPY (temp_pointer, linked_list->data.address16.resource_source); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (STRLEN (linked_list->data.address16.resource_source) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + actual_bytes = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + *bytes_consumed = actual_bytes; + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + actual_bytes -= 3; + MOVE_UNALIGNED16_TO_16 (length_field, &actual_bytes); + + return (AE_OK); +} + +/*************************************************************************** + * FUNCTION: Acpi_rs_address32_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_address32_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (ADDRESS32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + u32 index; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + *bytes_consumed = temp16 + 3; + + output_struct->id = address32; + + /* + * Get the Resource Type (Byte3) + */ + buffer += 2; + temp8 = *buffer; + + /* Values 0-2 are valid */ + if(temp8 > 2) { + return (AE_ERROR); + } + + output_struct->data.address32.resource_type = temp8 & 0x03; + + /* + * Get the General Flags (Byte4) + */ + buffer += 1; + temp8 = *buffer; + + /* + * Producer / Consumer + */ + output_struct->data.address32.producer_consumer = temp8 & 0x01; + + /* + * Decode + */ + output_struct->data.address32.decode = (temp8 >> 1) & 0x01; + + /* + * Min Address Fixed + */ + output_struct->data.address32.min_address_fixed = (temp8 >> 2) & 0x01; + + /* + * Max Address Fixed + */ + output_struct->data.address32.max_address_fixed = (temp8 >> 3) & 0x01; + + /* + * Get the Type Specific Flags (Byte5) + */ + buffer += 1; + temp8 = *buffer; + + if (MEMORY_RANGE == output_struct->data.address32.resource_type) { + output_struct->data.address32.attribute.memory.read_write_attribute = + (u16) (temp8 & 0x01); + + output_struct->data.address32.attribute.memory.cache_attribute = + (u16) ((temp8 >> 1) & 0x0F); + } + + else { + if (IO_RANGE == output_struct->data.address32.resource_type) { + output_struct->data.address32.attribute.io.range_attribute = + (u16) (temp8 & 0x03); + } + + else { + /* BUS_NUMBER_RANGE == Output_struct->Data.Address32.Resource_type */ + /* Nothing needs to be filled in */ + } + } + + /* + * Get Granularity (Bytes 6-9) + */ + buffer += 1; + MOVE_UNALIGNED32_TO_32 (&output_struct->data.address32.granularity, + buffer); + + /* + * Get Min_address_range (Bytes 10-13) + */ + buffer += 4; + MOVE_UNALIGNED32_TO_32 (&output_struct->data.address32.min_address_range, + buffer); + + /* + * Get Max_address_range (Bytes 14-17) + */ + buffer += 4; + MOVE_UNALIGNED32_TO_32 (&output_struct->data.address32.max_address_range, + buffer); + + /* + * Get Address_translation_offset (Bytes 18-21) + */ + buffer += 4; + MOVE_UNALIGNED32_TO_32 + (&output_struct->data.address32.address_translation_offset, + buffer); + + /* + * Get Address_length (Bytes 22-25) + */ + buffer += 4; + MOVE_UNALIGNED32_TO_32 (&output_struct->data.address32.address_length, + buffer); + + /* + * Resource Source Index (if present) + */ + buffer += 4; + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + */ + if (*bytes_consumed > 26) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.address32.resource_source_index = (u32)temp8; + + /* Point to the String */ + + buffer += 1; + + /* Copy the string into the buffer */ + + index = 0; + + while (0x00 != *buffer) { + output_struct->data.address32.resource_source[index] = *buffer; + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + output_struct->data.address32.resource_source[index] = 0x00; + + output_struct->data.address32.resource_source_string_length = index + 1; + + /* + * In order for the Struct_size to fall on a 32-bit boundry, + * calculate the length of the string and expand the + * Struct_size to the next 32-bit boundry. + */ + temp8 = (u8) (index + 1); + struct_size += ROUND_UP_TO_32_bITS (temp8); + } + + else { + output_struct->data.address32.resource_source_index = 0x00; + output_struct->data.address32.resource_source_string_length = 0; + output_struct->data.address32.resource_source[0] = 0x00; + } + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_address32_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_address32_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 *length_field; + u8 temp8 = 0; + u8 *temp_pointer = NULL; + + + /* + * The descriptor field is static + */ + *buffer = 0x87; + + buffer += 1; + + /* + * Set a pointer to the Length field - to be filled in later + */ + + length_field = (u16 *)buffer; + + buffer += 2; + + /* + * Set the Resource Type (Memory, Io, Bus_number) + */ + temp8 = (u8) (linked_list->data.address32.resource_type & 0x03); + + *buffer = temp8; + + buffer += 1; + + /* + * Set the general flags + */ + temp8 = (u8) (linked_list->data.address32.producer_consumer & 0x01); + + temp8 |= (linked_list->data.address32.decode & 0x01) << 1; + + temp8 |= (linked_list->data.address32.min_address_fixed & 0x01) << 2; + + temp8 |= (linked_list->data.address32.max_address_fixed & 0x01) << 3; + + *buffer = temp8; + + buffer += 1; + + /* + * Set the type specific flags + */ + temp8 = 0; + + if(MEMORY_RANGE == linked_list->data.address32.resource_type) { + temp8 = (u8) + (linked_list->data.address32.attribute.memory.read_write_attribute & + 0x01); + + temp8 |= + (linked_list->data.address32.attribute.memory.cache_attribute & + 0x0F) << 1; + } + + else if (IO_RANGE == linked_list->data.address32.resource_type) { + temp8 = (u8) + (linked_list->data.address32.attribute.io.range_attribute & + 0x03); + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the address space granularity + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.address32.granularity); + buffer += 4; + + /* + * Set the address range minimum + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.address32.min_address_range); + + buffer += 4; + + /* + * Set the address range maximum + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.address32.max_address_range); + + buffer += 4; + + /* + * Set the address translation offset + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.address32.address_translation_offset); + + buffer += 4; + + /* + * Set the address length + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.address32.address_length); + + buffer += 4; + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.address32.resource_source_string_length) { + temp8 = (u8) linked_list->data.address32.resource_source_index; + + *buffer = temp8; + + buffer += 1; + + temp_pointer = buffer; + + /* + * Copy the string + */ + STRCPY (temp_pointer, linked_list->data.address32.resource_source); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (STRLEN (linked_list->data.address32.resource_source) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + *length_field = (u16) (*bytes_consumed - 3); + + return (AE_OK); +} + diff --git a/drivers/acpi/resources/rscalc.c b/drivers/acpi/resources/rscalc.c new file mode 100644 index 000000000..3893d2799 --- /dev/null +++ b/drivers/acpi/resources/rscalc.c @@ -0,0 +1,754 @@ +/****************************************************************************** + * + * Module Name: rscalc - Acpi_rs_calculate_byte_stream_length + * Acpi_rs_calculate_list_length + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rscalc"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_calculate_byte_stream_length + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Size_needed - u32 pointer of the size buffer needed + * to properly return the parsed data + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Takes the resource byte stream and parses it once, calculating + * the size buffer needed to hold the linked list that conveys + * the resource data. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_calculate_byte_stream_length ( + RESOURCE *linked_list, + u32 *size_needed) +{ + ACPI_STATUS status = AE_OK; + u32 byte_stream_size_needed = 0; + u32 size_of_this_bit; + EXTENDED_IRQ_RESOURCE *ex_irq = NULL; + u8 done = FALSE; + + + while (!done) { + + /* + * Init the variable that will hold the size to add to the + * total. + */ + size_of_this_bit = 0; + + switch (linked_list->id) + { + case irq: + /* + * IRQ Resource + */ + /* + * For an IRQ Resource, Byte 3, although optional, will + * always be created - it holds IRQ information. + */ + size_of_this_bit = 4; + break; + + case dma: + /* + * DMA Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 3; + break; + + case start_dependent_functions: + /* + * Start Dependent Functions Resource + */ + /* + * For a Start_dependent_functions Resource, Byte 1, + * although optional, will always be created. + */ + size_of_this_bit = 2; + break; + + case end_dependent_functions: + /* + * End Dependent Functions Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 1; + break; + + case io: + /* + * IO Port Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 8; + break; + + case fixed_io: + /* + * Fixed IO Port Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 4; + break; + + case vendor_specific: + /* + * Vendor Defined Resource + */ + /* + * For a Vendor Specific resource, if the Length is + * between 1 and 7 it will be created as a Small + * Resource data type, otherwise it is a Large + * Resource data type. + */ + if(linked_list->data.vendor_specific.length > 7) { + size_of_this_bit = 3; + } + else { + size_of_this_bit = 1; + } + size_of_this_bit += + linked_list->data.vendor_specific.length; + break; + + case end_tag: + /* + * End Tag + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 2; + done = TRUE; + break; + + case memory24: + /* + * 24-Bit Memory Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 12; + break; + + case memory32: + /* + * 32-Bit Memory Range Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 20; + break; + + case fixed_memory32: + /* + * 32-Bit Fixed Memory Resource + */ + /* + * For this resource the size is static + */ + size_of_this_bit = 12; + break; + + case address16: + /* + * 16-Bit Address Resource + */ + /* + * The base size of this byte stream is 16. If a + * Resource Source string is not NULL, add 1 for + * the Index + the length of the null terminated + * string Resource Source + 1 for the null. + */ + size_of_this_bit = 16; + + if(NULL != linked_list->data.address16.resource_source) { + size_of_this_bit += (1 + + linked_list->data.address16.resource_source_string_length); + } + break; + + case address32: + /* + * 32-Bit Address Resource + */ + /* + * The base size of this byte stream is 26. If a Resource + * Source string is not NULL, add 1 for the Index + the + * length of the null terminated string Resource Source + + * 1 for the null. + */ + size_of_this_bit = 26; + + if(NULL != linked_list->data.address16.resource_source) { + size_of_this_bit += (1 + + linked_list->data.address16.resource_source_string_length); + } + break; + + case extended_irq: + /* + * Extended IRQ Resource + */ + /* + * The base size of this byte stream is 9. This is for an + * Interrupt table length of 1. For each additional + * interrupt, add 4. + * If a Resource Source string is not NULL, add 1 for the + * Index + the length of the null terminated string + * Resource Source + 1 for the null. + */ + size_of_this_bit = 9; + + size_of_this_bit += + (linked_list->data.extended_irq.number_of_interrupts - + 1) * 4; + + if(NULL != ex_irq->resource_source) { + size_of_this_bit += (1 + + linked_list->data.extended_irq.resource_source_string_length); + } + break; + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_ERROR); + break; + + } /* switch (Linked_list->Id) */ + + /* + * Update the total + */ + byte_stream_size_needed += size_of_this_bit; + + /* + * Point to the next object + */ + linked_list = (RESOURCE *) ((NATIVE_UINT) linked_list + + (NATIVE_UINT) linked_list->length); + } + + /* + * This is the data the caller needs + */ + *size_needed = byte_stream_size_needed; + + return (status); + +} /* Acpi_rs_calculate_byte_stream_length */ + + +/*************************************************************************** + * FUNCTION: Acpi_rs_calculate_list_length + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource byte stream + * Byte_stream_buffer_length - Size of Byte_stream_buffer + * Size_needed - u32 pointer of the size buffer + * needed to properly return the + * parsed data + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Takes the resource byte stream and parses it once, calculating + * the size buffer needed to hold the linked list that conveys + * the resource data. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_calculate_list_length ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + u32 *size_needed) +{ + u32 buffer_size = 0; + u32 bytes_parsed = 0; + u8 resource_type = 0; + u32 structure_size = 0; + u32 bytes_consumed = 0; + u8 *buffer; + u8 number_of_interrupts = 0; + u8 temp8; + u16 temp16; + u8 index; + u8 number_of_channels = 0; + u8 additional_bytes = 0; + + + while (bytes_parsed < byte_stream_buffer_length) { + /* + * Look at the next byte in the stream + */ + resource_type = *byte_stream_buffer; + + /* + * See if this is a small or large resource + */ + if(resource_type & 0x80) { + /* + * Large Resource Type + */ + switch (resource_type) + { + case MEMORY_RANGE_24: + /* + * 24-Bit Memory Resource + */ + bytes_consumed = 12; + + structure_size = sizeof (MEMORY24_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + break; + + case LARGE_VENDOR_DEFINED: + /* + * Vendor Defined Resource + */ + buffer = byte_stream_buffer; + ++buffer; + + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + bytes_consumed = temp16 + 3; + + /* + * Ensure a 32-bit boundary for the structure + */ + temp16 = (u16) ROUND_UP_TO_32_bITS (temp16); + + structure_size = sizeof (VENDOR_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (temp16 * sizeof (u8)); + break; + + case MEMORY_RANGE_32: + /* + * 32-Bit Memory Range Resource + */ + + bytes_consumed = 20; + + structure_size = sizeof (MEMORY32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + break; + + case FIXED_MEMORY_RANGE_32: + /* + * 32-Bit Fixed Memory Resource + */ + bytes_consumed = 12; + + structure_size = sizeof(FIXED_MEMORY32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + break; + + case DWORD_ADDRESS_SPACE: + /* + * 32-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Resource Source Index and Resource Source are + * optional elements. Check the length of the + * Bytestream. If it is greater than 23, that + * means that an Index exists and is followed by + * a null termininated string. Therefore, set + * the temp variable to the length minus the minimum + * byte stream length plus the byte for the Index to + * determine the size of the NULL terminiated string. + */ + if (23 < temp16) { + temp8 = (u8) (temp16 - 24); + } + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundary for the structure + */ + temp8 = (u8) ROUND_UP_TO_32_bITS (temp8); + + structure_size = sizeof (ADDRESS32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (temp8 * sizeof (u8)); + break; + + case WORD_ADDRESS_SPACE: + /* + * 16-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Resource Source Index and Resource Source are + * optional elements. Check the length of the + * Bytestream. If it is greater than 13, that + * means that an Index exists and is followed by + * a null termininated string. Therefore, set + * the temp variable to the length minus the minimum + * byte stream length plus the byte for the Index to + * determine the size of the NULL terminiated string. + */ + if (13 < temp16) { + temp8 = (u8) (temp16 - 14); + } + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundry for the structure + */ + temp8 = (u8) ROUND_UP_TO_32_bITS (temp8); + + structure_size = sizeof (ADDRESS16_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (temp8 * sizeof (u8)); + break; + + case EXTENDED_IRQ: + /* + * Extended IRQ + */ + buffer = byte_stream_buffer; + + ++buffer; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Point past the length field and the + * Interrupt vector flags to save off the + * Interrupt table length to the Temp8 variable. + */ + buffer += 3; + + temp8 = *buffer; + + /* + * To compensate for multiple interrupt numbers, + * Add 4 bytes for each additional interrupts + * greater than 1 + */ + additional_bytes = (u8) ((temp8 - 1) * 4); + + /* + * Resource Source Index and Resource Source are + * optional elements. Check the length of the + * Bytestream. If it is greater than 9, that + * means that an Index exists and is followed by + * a null termininated string. Therefore, set + * the temp variable to the length minus the minimum + * byte stream length plus the byte for the Index to + * determine the size of the NULL terminiated string. + */ + if (9 + additional_bytes < temp16) { + temp8 = (u8) (temp16 - (9 + additional_bytes)); + } + + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundry for the structure + */ + temp8 = (u8) ROUND_UP_TO_32_bITS (temp8); + + structure_size = sizeof (EXTENDED_IRQ_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (additional_bytes * sizeof (u8)) + + (temp8 * sizeof (u8)); + + break; + +/* 64-bit not currently supported */ +/* + case 0x8A: + break; +*/ + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_ERROR); + break; + } + } + + else { + /* + * Small Resource Type + * Only bits 7:3 are valid + */ + resource_type >>= 3; + + switch (resource_type) + { + case IRQ_FORMAT: + /* + * IRQ Resource + */ + /* + * Determine if it there are two or three + * trailing bytes + */ + buffer = byte_stream_buffer; + + temp8 = *buffer; + + if(temp8 & 0x01) { + bytes_consumed = 4; + } + + else { + bytes_consumed = 3; + } + + /* + * Point past the descriptor + */ + ++buffer; + + /* + * Look at the number of bits set + */ + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + for (index = 0; index < 16; index++) { + if (temp16 & 0x1) { + ++number_of_interrupts; + } + + temp16 >>= 1; + } + + structure_size = sizeof (IO_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (number_of_interrupts * sizeof (u32)); + + break; + + + case DMA_FORMAT: + + /* + * DMA Resource + */ + buffer = byte_stream_buffer; + + bytes_consumed = 3; + + /* + * Point past the descriptor + */ + ++buffer; + + /* + * Look at the number of bits set + */ + temp8 = *buffer; + + for(index = 0; index < 8; index++) { + if(temp8 & 0x1) { + ++number_of_channels; + } + + temp8 >>= 1; + } + + structure_size = sizeof (DMA_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (number_of_channels * sizeof (u32)); + + break; + + + case START_DEPENDENT_TAG: + + /* + * Start Dependent Functions Resource + */ + /* + * Determine if it there are two or three trailing bytes + */ + buffer = byte_stream_buffer; + + temp8 = *buffer; + + if(temp8 & 0x01) { + bytes_consumed = 2; + } + else { + bytes_consumed = 1; + } + + + structure_size = + sizeof (START_DEPENDENT_FUNCTIONS_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + break; + + + case END_DEPENDENT_TAG: + + /* + * End Dependent Functions Resource + */ + bytes_consumed = 1; + + structure_size = RESOURCE_LENGTH; + break; + + + case IO_PORT_DESCRIPTOR: + /* + * IO Port Resource + */ + bytes_consumed = 8; + + structure_size = sizeof (IO_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + break; + + + case FIXED_LOCATION_IO_DESCRIPTOR: + + /* + * Fixed IO Port Resource + */ + bytes_consumed = 4; + + structure_size = sizeof (FIXED_IO_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + break; + + + case SMALL_VENDOR_DEFINED: + + /* + * Vendor Specific Resource + */ + buffer = byte_stream_buffer; + + temp8 = *buffer; + temp8 = (u8) (temp8 & 0x7); + bytes_consumed = temp8 + 1; + + /* + * Ensure a 32-bit boundry for the structure + */ + temp8 = (u8) ROUND_UP_TO_32_bITS (temp8); + + structure_size = sizeof (VENDOR_RESOURCE) + + RESOURCE_LENGTH_NO_DATA + + (temp8 * sizeof (u8)); + break; + + + case END_TAG: + + /* + * End Tag + */ + bytes_consumed = 2; + + structure_size = RESOURCE_LENGTH; + break; + + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_ERROR); + break; + + } /* switch */ + + } /* if(Resource_type & 0x80) */ + + /* + * Update the return value and counter + */ + buffer_size += structure_size; + bytes_parsed += bytes_consumed; + + /* + * Set the byte stream to point to the next resource + */ + byte_stream_buffer += bytes_consumed; + + } + + /* + * This is the data the caller needs + */ + *size_needed = buffer_size; + + return (AE_OK); + +} /* Acpi_rs_calculate_list_length */ + diff --git a/drivers/acpi/resources/rscreate.c b/drivers/acpi/resources/rscreate.c new file mode 100644 index 000000000..3bb46eb48 --- /dev/null +++ b/drivers/acpi/resources/rscreate.c @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * Module Name: rscreate - Acpi_rs_create_resource_list + * Acpi_rs_create_pci_routing_table + * Acpi_rs_create_byte_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "resource.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rscreate"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_create_resource_list + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource byte stream + * Output_buffer - Pointer to the user's buffer + * Output_buffer_length - Pointer to the size of Output_buffer + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * If Output_buffer is not large enough, Output_buffer_length + * indicates how large Output_buffer should be, else it + * indicates how may u8 elements of Output_buffer are + * valid. + * + * DESCRIPTION: Takes the byte stream returned from a _CRS, _PRS control method + * execution and parses the stream to create a linked list + * of device resources. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_create_resource_list ( + ACPI_OBJECT_INTERNAL *byte_stream_buffer, + u8 *output_buffer, + u32 *output_buffer_length) +{ + + ACPI_STATUS status = AE_UNKNOWN_STATUS; + u8 *byte_stream_start = NULL; + u32 list_size_needed = 0; + u32 byte_stream_buffer_length = 0; + + + /* + * Validate parameters: + * + * 1. If Byte_stream_buffer is NULL after we know that + * Byte_steam_length is not zero, or + * 2. If Output_buffer is NULL and Output_buffer_length + * is not zero + * + * Return an error + */ + if (!byte_stream_buffer || + (!output_buffer && 0 != *output_buffer_length)) + { + return (AE_BAD_PARAMETER); + } + + byte_stream_buffer_length = byte_stream_buffer->buffer.length; + byte_stream_start = byte_stream_buffer->buffer.pointer; + + /* + * Pass the Byte_stream_buffer into a module that can calculate + * the buffer size needed for the linked list + */ + status = acpi_rs_calculate_list_length (byte_stream_start, + byte_stream_buffer_length, + &list_size_needed); + + /* + * Exit with the error passed back + */ + if (AE_OK != status) { + return (status); + } + + /* + * If the linked list will fit into the available buffer + * call to fill in the list + */ + + if (list_size_needed <= *output_buffer_length) { + /* + * Zero out the return buffer before proceeding + */ + MEMSET (output_buffer, 0x00, *output_buffer_length); + + status = acpi_rs_byte_stream_to_list (byte_stream_start, + byte_stream_buffer_length, + &output_buffer); + + /* + * Exit with the error passed back + */ + if (AE_OK != status) { + return (status); + } + + } + + else { + *output_buffer_length = list_size_needed; + return (AE_BUFFER_OVERFLOW); + } + + *output_buffer_length = list_size_needed; + return (AE_OK); + +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_create_pci_routing_table + * + * PARAMETERS: + * Package_object - Pointer to an ACPI_OBJECT_INTERNAL + * package + * Output_buffer - Pointer to the user's buffer + * Output_buffer_length - Size of Output_buffer + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code. + * If the Output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and Output_buffer_length will point + * to the size buffer needed. + * + * DESCRIPTION: Takes the ACPI_OBJECT_INTERNAL package and creates a + * linked list of PCI interrupt descriptions + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_create_pci_routing_table ( + ACPI_OBJECT_INTERNAL *package_object, + u8 *output_buffer, + u32 *output_buffer_length) +{ + u8 *buffer = output_buffer; + ACPI_OBJECT_INTERNAL **top_object_list = NULL; + ACPI_OBJECT_INTERNAL **sub_object_list = NULL; + ACPI_OBJECT_INTERNAL *package_element = NULL; + u32 buffer_size_needed = 0; + u32 number_of_elements = 0; + u32 index = 0; + u8 table_index = 0; + u8 name_found = FALSE; + PCI_ROUTING_TABLE *user_prt = NULL; + + + /* + * Validate parameters: + * + * 1. If Method_return_object is NULL, or + * 2. If Output_buffer is NULL and Output_buffer_length is not zero + * + * Return an error + */ + if (!package_object || + (!output_buffer && 0 != *output_buffer_length)) + { + return (AE_BAD_PARAMETER); + } + + /* + * Calculate the buffer size needed for the routing table. + */ + number_of_elements = package_object->package.count; + + /* + * Properly calculate the size of the return buffer. + * The base size is the number of elements * the sizes of the + * structures. Additional space for the strings is added below. + * The minus one is to subtract the size of the u8 Source[1] + * member because it is added below. + * NOTE: The Number_of_elements is incremented by one to add an end + * table structure that is essentially a structure of zeros. + */ + buffer_size_needed = (number_of_elements + 1) * + (sizeof (PCI_ROUTING_TABLE) - 1); + + /* + * But each PRT_ENTRY structure has a pointer to a string and + * the size of that string must be found. + */ + top_object_list = package_object->package.elements; + + for (index = 0; index < number_of_elements; index++) { + /* + * Dereference the sub-package + */ + package_element = *top_object_list; + + /* + * The Sub_object_list will now point to an array of the + * four IRQ elements: Address, Pin, Source and Source_index + */ + sub_object_list = package_element->package.elements; + + /* + * Scan the Irq_table_elements for the Source Name String + */ + name_found = FALSE; + + for (table_index = 0; table_index < 4 && !name_found; table_index++) { + if (ACPI_TYPE_STRING == (*sub_object_list)->common.type) { + name_found = TRUE; + } + + else { + /* + * Look at the next element + */ + sub_object_list++; + } + } + + /* + * Was a String type found? + */ + if (TRUE == name_found) { + /* + * The length String.Length field includes the + * terminating NULL + */ + buffer_size_needed += ((*sub_object_list)->string.length); + } + + else { + /* + * If no name was found, then this is a NULL, which is + * translated as a u32 zero. + */ + buffer_size_needed += sizeof(u32); + } + + /* + * Point to the next ACPI_OBJECT_INTERNAL + */ + top_object_list++; + } + + /* + * If the data will fit into the available buffer + * call to fill in the list + */ + if (buffer_size_needed <= *output_buffer_length) { + /* + * Zero out the return buffer before proceeding + */ + MEMSET (output_buffer, 0x00, *output_buffer_length); + + /* + * Loop through the ACPI_INTERNAL_OBJECTS - Each object should + * contain a u32 Address, a u8 Pin, a Name and a u8 + * Source_index. + */ + top_object_list = package_object->package.elements; + + number_of_elements = package_object->package.count; + + user_prt = (PCI_ROUTING_TABLE *)buffer; + + for (index = 0; index < number_of_elements; index++) { + /* + * Point User_prt past this current structure + * + * NOTE: On the first iteration, User_prt->Length will + * be zero because we zero'ed out the return buffer + * earlier + */ + buffer += user_prt->length; + + user_prt = (PCI_ROUTING_TABLE *)buffer; + + /* + * Fill in the Length field with the information we + * have at this point. + * The minus one is to subtract the size of the + * u8 Source[1] member because it is added below. + */ + user_prt->length = (sizeof(PCI_ROUTING_TABLE) - 1); + + /* + * Dereference the sub-package + */ + package_element = *top_object_list; + + /* + * The Sub_object_list will now point to an array of + * the four IRQ elements: Address, Pin, Source and + * Source_index + */ + sub_object_list = package_element->package.elements; + + /* + * Dereference the Address + */ + if (ACPI_TYPE_NUMBER == (*sub_object_list)->common.type) { + user_prt->data.address = + (*sub_object_list)->number.value; + } + + else { + return (AE_BAD_DATA); + } + + /* + * Dereference the Pin + */ + sub_object_list++; + + if (ACPI_TYPE_NUMBER == (*sub_object_list)->common.type) { + user_prt->data.pin = + (*sub_object_list)->number.value; + } + + else { + return (AE_BAD_DATA); + } + + /* + * Dereference the Source Name + */ + sub_object_list++; + + if (ACPI_TYPE_STRING == (*sub_object_list)->common.type) { + STRCPY(user_prt->data.source, + (*sub_object_list)->string.pointer); + + /* + * Add to the Length field the length of the string + */ + user_prt->length += (*sub_object_list)->string.length; + } + + else { + /* + * If this is a number, then the Source Name + * is NULL, since the entire buffer was zeroed + * out, we can leave this alone. + */ + if (ACPI_TYPE_NUMBER == (*sub_object_list)->common.type) { + /* + * Add to the Length field the length of + * the u32 NULL + */ + user_prt->length += sizeof(u32); + } + + else { + return (AE_BAD_DATA); + } + } + + /* + * Dereference the Source Index + */ + sub_object_list++; + + if (ACPI_TYPE_NUMBER == (*sub_object_list)->common.type) { + user_prt->data.source_index = + (*sub_object_list)->number.value; + } + + else { + return (AE_BAD_DATA); + } + + /* + * Point to the next ACPI_OBJECT_INTERNAL + */ + top_object_list++; + } + + } + + else { + *output_buffer_length = buffer_size_needed; + + return (AE_BUFFER_OVERFLOW); + } + + /* + * Report the amount of buffer used + */ + *output_buffer_length = buffer_size_needed; + + return (AE_OK); + +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_create_byte_stream + * + * PARAMETERS: + * Linked_list_buffer - Pointer to the resource linked list + * Output_buffer - Pointer to the user's buffer + * Output_buffer_length - Size of Output_buffer + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code. + * If the Output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and Output_buffer_length will point + * to the size buffer needed. + * + * DESCRIPTION: Takes the linked list of device resources and + * creates a bytestream to be used as input for the + * _SRS control method. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_create_byte_stream ( + RESOURCE *linked_list_buffer, + u8 *output_buffer, + u32 *output_buffer_length) +{ + ACPI_STATUS status = AE_UNKNOWN_STATUS; + u32 byte_stream_size_needed = 0; + + + /* + * Validate parameters: + * + * 1. If Linked_list_buffer is NULL, or + * 2. If Output_buffer is NULL and Output_buffer_length is not zero + * + * Return an error + */ + if (!linked_list_buffer || + (!output_buffer && 0 != *output_buffer_length)) + { + return (AE_BAD_PARAMETER); + } + + /* + * Pass the Linked_list_buffer into a module that can calculate + * the buffer size needed for the byte stream. + */ + status = acpi_rs_calculate_byte_stream_length (linked_list_buffer, + &byte_stream_size_needed); + + /* + * Exit with the error passed back + */ + if (AE_OK != status) { + return (status); + } + + /* + * If the linked list will fit into the available buffer + * call to fill in the list + */ + + if (byte_stream_size_needed <= *output_buffer_length) { + /* + * Zero out the return buffer before proceeding + */ + MEMSET (output_buffer, 0x00, *output_buffer_length); + + status = acpi_rs_list_to_byte_stream (linked_list_buffer, + byte_stream_size_needed, + &output_buffer); + + /* + * Exit with the error passed back + */ + if (AE_OK != status) { + return (status); + } + + } + else { + *output_buffer_length = byte_stream_size_needed; + return (AE_BUFFER_OVERFLOW); + } + + return (AE_OK); + +} + diff --git a/drivers/acpi/resources/rsdump.c b/drivers/acpi/resources/rsdump.c new file mode 100644 index 000000000..490b6e167 --- /dev/null +++ b/drivers/acpi/resources/rsdump.c @@ -0,0 +1,938 @@ +/****************************************************************************** + * + * Module Name: rsdump - Functions do dump out the resource structures. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsdump"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_irq + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_irq ( + RESOURCE_DATA *data) +{ + IRQ_RESOURCE *irq_data = (IRQ_RESOURCE*) data; + u8 index = 0; + + + acpi_os_printf ("\t_iRQ Resource\n"); + + acpi_os_printf ("\t\t%s Triggered\n", + LEVEL_SENSITIVE == irq_data->edge_level ? + "Level" : "Edge"); + + acpi_os_printf ("\t\t_active %s\n", + ACTIVE_LOW == irq_data->active_high_low ? + "Low" : "High"); + + acpi_os_printf ("\t\t%s\n", + SHARED == irq_data->shared_exclusive ? + "Shared" : "Exclusive"); + + acpi_os_printf ("\t\t%d Interrupts ( ", + irq_data->number_of_interrupts); + + for (index = 0; index < irq_data->number_of_interrupts; index++) { + acpi_os_printf ("%d ", irq_data->interrupts[index]); + } + + acpi_os_printf (")\n"); + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_dma + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_dma ( + RESOURCE_DATA *data) +{ + DMA_RESOURCE *dma_data = (DMA_RESOURCE*) data; + u8 index = 0; + + + acpi_os_printf ("\t_dMA Resource\n"); + + switch (dma_data->type) + { + case COMPATIBILITY: + acpi_os_printf ("\t\t_compatibility mode\n"); + break; + + case TYPE_A: + acpi_os_printf ("\t\t_type A\n"); + break; + + case TYPE_B: + acpi_os_printf ("\t\t_type B\n"); + break; + + case TYPE_F: + acpi_os_printf ("\t\t_type F\n"); + break; + + default: + acpi_os_printf ("\t\t_invalid DMA type\n"); + break; + } + + acpi_os_printf ("\t\t%s_bus Master\n", + BUS_MASTER == dma_data->bus_master ? + "" : "Not a "); + + switch (dma_data->transfer) + { + case TRANSFER_8: + acpi_os_printf ("\t\t8-bit only transfer\n"); + break; + + case TRANSFER_8_16: + acpi_os_printf ("\t\t8 and 16-bit transfer\n"); + break; + + case TRANSFER_16: + acpi_os_printf ("\t\t16 bit only transfer\n"); + break; + + default: + acpi_os_printf ("\t\t_invalid transfer preference\n"); + break; + } + + acpi_os_printf ("\t\t_number of Channels: %d ( ", + dma_data->number_of_channels); + + for (index = 0; index < dma_data->number_of_channels; index++) { + acpi_os_printf ("%d ", dma_data->channels[index]); + } + + acpi_os_printf (")\n"); + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_start_dependent_functions + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_start_dependent_functions ( + RESOURCE_DATA *data) +{ + START_DEPENDENT_FUNCTIONS_RESOURCE *sdf_data = + (START_DEPENDENT_FUNCTIONS_RESOURCE*) data; + + + acpi_os_printf ("\t_start Dependent Functions Resource\n"); + + switch (sdf_data->compatibility_priority) + { + case GOOD_CONFIGURATION: + acpi_os_printf ("\t\t_good configuration\n"); + break; + + case ACCEPTABLE_CONFIGURATION: + acpi_os_printf ("\t\t_acceptable configuration\n"); + break; + + case SUB_OPTIMAL_CONFIGURATION: + acpi_os_printf ("\t\t_sub-optimal configuration\n"); + break; + + default: + acpi_os_printf ("\t\t_invalid compatibility priority\n"); + break; + } + + switch(sdf_data->performance_robustness) + { + case GOOD_CONFIGURATION: + acpi_os_printf ("\t\t_good configuration\n"); + break; + + case ACCEPTABLE_CONFIGURATION: + acpi_os_printf ("\t\t_acceptable configuration\n"); + break; + + case SUB_OPTIMAL_CONFIGURATION: + acpi_os_printf ("\t\t_sub-optimal configuration\n"); + break; + + default: + acpi_os_printf ("\t\t_invalid performance " + "robustness preference\n"); + break; + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_io + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_io ( + RESOURCE_DATA *data) +{ + IO_RESOURCE *io_data = (IO_RESOURCE*) data; + + + acpi_os_printf ("\t_io Resource\n"); + + acpi_os_printf ("\t\t%d bit decode\n", + DECODE_16 == io_data->io_decode ? 16 : 10); + + acpi_os_printf ("\t\t_range minimum base: 0x%08x\n", + io_data->min_base_address); + + acpi_os_printf ("\t\t_range maximum base: 0x%08x\n", + io_data->max_base_address); + + acpi_os_printf ("\t\t_alignment: 0x%08x\n", + io_data->alignment); + + acpi_os_printf ("\t\t_range Length: 0x%08x\n", + io_data->range_length); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_fixed_io + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_fixed_io ( + RESOURCE_DATA *data) +{ + FIXED_IO_RESOURCE *fixed_io_data = (FIXED_IO_RESOURCE*) data; + + + acpi_os_printf ("\t_fixed Io Resource\n"); + acpi_os_printf ("\t\t_range base address: 0x%08x", + fixed_io_data->base_address); + + acpi_os_printf ("\t\t_range length: 0x%08x", + fixed_io_data->range_length); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_vendor_specific + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_vendor_specific ( + RESOURCE_DATA *data) +{ + VENDOR_RESOURCE *vendor_data = (VENDOR_RESOURCE*) data; + u16 index = 0; + + + acpi_os_printf ("\t_vendor Specific Resource\n"); + + acpi_os_printf ("\t\t_length: 0x%08x\n", vendor_data->length); + + for (index = 0; index < vendor_data->length; index++) { + acpi_os_printf ("\t\t_byte %d: 0x%08x\n", + index, vendor_data->reserved[index]); + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_memory24 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_memory24 ( + RESOURCE_DATA *data) +{ + MEMORY24_RESOURCE *memory24_data = (MEMORY24_RESOURCE*) data; + + + acpi_os_printf ("\t24-Bit Memory Range Resource\n"); + + acpi_os_printf ("\t\t_read%s\n", + READ_WRITE_MEMORY == + memory24_data->read_write_attribute ? + "/Write" : " only"); + + acpi_os_printf ("\t\t_range minimum base: 0x%08x\n", + memory24_data->min_base_address); + + acpi_os_printf ("\t\t_range maximum base: 0x%08x\n", + memory24_data->max_base_address); + + acpi_os_printf ("\t\t_alignment: 0x%08x\n", + memory24_data->alignment); + + acpi_os_printf ("\t\t_range length: 0x%08x\n", + memory24_data->range_length); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_memory32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_memory32 ( + RESOURCE_DATA *data) +{ + MEMORY32_RESOURCE *memory32_data = (MEMORY32_RESOURCE*) data; + + + acpi_os_printf ("\t32-Bit Memory Range Resource\n"); + + acpi_os_printf ("\t\t_read%s\n", + READ_WRITE_MEMORY == + memory32_data->read_write_attribute ? + "/Write" : " only"); + + acpi_os_printf ("\t\t_range minimum base: 0x%08x\n", + memory32_data->min_base_address); + + acpi_os_printf ("\t\t_range maximum base: 0x%08x\n", + memory32_data->max_base_address); + + acpi_os_printf ("\t\t_alignment: 0x%08x\n", + memory32_data->alignment); + + acpi_os_printf ("\t\t_range length: 0x%08x\n", + memory32_data->range_length); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_fixed_memory32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_fixed_memory32 ( + RESOURCE_DATA *data) +{ + FIXED_MEMORY32_RESOURCE *fixed_memory32_data = (FIXED_MEMORY32_RESOURCE*) data; + + + acpi_os_printf ("\t32-Bit Fixed Location Memory Range Resource\n"); + + acpi_os_printf ("\t\t_read%s\n", + READ_WRITE_MEMORY == + fixed_memory32_data->read_write_attribute ? + "/Write" : " Only"); + + acpi_os_printf ("\t\t_range base address: 0x%08x\n", + fixed_memory32_data->range_base_address); + + acpi_os_printf ("\t\t_range length: 0x%08x\n", + fixed_memory32_data->range_length); + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_address16 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_address16 ( + RESOURCE_DATA *data) +{ + ADDRESS16_RESOURCE *address16_data = (ADDRESS16_RESOURCE*) data; + + + acpi_os_printf ("\t16-Bit Address Space Resource\n"); + acpi_os_printf ("\t\t_resource Type: "); + + switch (address16_data->resource_type) + { + case MEMORY_RANGE: + + acpi_os_printf ("Memory Range\n"); + + switch (address16_data->attribute.memory.cache_attribute) + { + case NON_CACHEABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Noncacheable memory\n"); + break; + + case CACHABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Cacheable memory\n"); + break; + + case WRITE_COMBINING_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Write-combining memory\n"); + break; + + case PREFETCHABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Prefetchable memory\n"); + break; + + default: + acpi_os_printf ("\t\t_type Specific: " + "Invalid cache attribute\n"); + break; + } + + acpi_os_printf ("\t\t_type Specific: Read%s\n", + READ_WRITE_MEMORY == + address16_data->attribute.memory.read_write_attribute ? + "/Write" : " Only"); + break; + + case IO_RANGE: + + acpi_os_printf ("I/O Range\n"); + + switch (address16_data->attribute.io.range_attribute) + { + case NON_ISA_ONLY_RANGES: + acpi_os_printf ("\t\t_type Specific: " + "Non-ISA Io Addresses\n"); + break; + + case ISA_ONLY_RANGES: + acpi_os_printf ("\t\t_type Specific: " + "ISA Io Addresses\n"); + break; + + case ENTIRE_RANGE: + acpi_os_printf ("\t\t_type Specific: " + "ISA and non-ISA Io Addresses\n"); + break; + + default: + acpi_os_printf ("\t\t_type Specific: " + "Invalid range attribute\n"); + break; + } + break; + + case BUS_NUMBER_RANGE: + + acpi_os_printf ("Bus Number Range\n"); + break; + + default: + + acpi_os_printf ("Invalid resource type. Exiting.\n"); + return; + } + + acpi_os_printf ("\t\t_resource %s\n", + CONSUMER == address16_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf ("\t\t%s decode\n", + SUB_DECODE == address16_data->decode ? + "Subtractive" : "Positive"); + + acpi_os_printf ("\t\t_min address is %s fixed\n", + ADDRESS_FIXED == address16_data->min_address_fixed ? + "" : "not"); + + acpi_os_printf ("\t\t_max address is %s fixed\n", + ADDRESS_FIXED == address16_data->max_address_fixed ? + "" : "not"); + + acpi_os_printf ("\t\t_granularity: 0x%08x\n", + address16_data->granularity); + + acpi_os_printf ("\t\t_address range min: 0x%08x\n", + address16_data->min_address_range); + + acpi_os_printf ("\t\t_address range max: 0x%08x\n", + address16_data->max_address_range); + + acpi_os_printf ("\t\t_address translation offset: 0x%08x\n", + address16_data->address_translation_offset); + + acpi_os_printf ("\t\t_address Length: 0x%08x\n", + address16_data->address_length); + + if (0xFF != address16_data->resource_source_index) { + acpi_os_printf ("\t\t_resource Source Index: %d\n", + address16_data->resource_source_index); + acpi_os_printf ("\t\t_resource Source: %s\n", + address16_data->resource_source); + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_address32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_address32 ( + RESOURCE_DATA *data) +{ + ADDRESS32_RESOURCE *address32_data = (ADDRESS32_RESOURCE*) data; + + + acpi_os_printf ("\t32-Bit Address Space Resource\n"); + + switch (address32_data->resource_type) + { + case MEMORY_RANGE: + + acpi_os_printf ("\t\t_resource Type: Memory Range\n"); + + switch (address32_data->attribute.memory.cache_attribute) + { + case NON_CACHEABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Noncacheable memory\n"); + break; + + case CACHABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Cacheable memory\n"); + break; + + case WRITE_COMBINING_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Write-combining memory\n"); + break; + + case PREFETCHABLE_MEMORY: + acpi_os_printf ("\t\t_type Specific: " + "Prefetchable memory\n"); + break; + + default: + acpi_os_printf ("\t\t_type Specific: " + "Invalid cache attribute\n"); + break; + } + + acpi_os_printf ("\t\t_type Specific: Read%s\n", + READ_WRITE_MEMORY == + address32_data->attribute.memory.read_write_attribute ? + "/Write" : " Only"); + break; + + case IO_RANGE: + + acpi_os_printf ("\t\t_resource Type: Io Range\n"); + + switch (address32_data->attribute.io.range_attribute) + { + case NON_ISA_ONLY_RANGES: + acpi_os_printf ("\t\t_type Specific: " + "Non-ISA Io Addresses\n"); + break; + + case ISA_ONLY_RANGES: + acpi_os_printf ("\t\t_type Specific: " + "ISA Io Addresses\n"); + break; + + case ENTIRE_RANGE: + acpi_os_printf ("\t\t_type Specific: " + "ISA and non-ISA Io Addresses\n"); + break; + + default: + acpi_os_printf ("\t\t_type Specific: " + "Invalid Range attribute"); + break; + } + break; + + case BUS_NUMBER_RANGE: + + acpi_os_printf ("\t\t_resource Type: Bus Number Range\n"); + break; + + default: + + acpi_os_printf ("\t\t_invalid Resource Type..exiting.\n"); + return; + } + + acpi_os_printf ("\t\t_resource %s\n", + CONSUMER == address32_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf ("\t\t%s decode\n", + SUB_DECODE == address32_data->decode ? + "Subtractive" : "Positive"); + + acpi_os_printf ("\t\t_min address is %s fixed\n", + ADDRESS_FIXED == address32_data->min_address_fixed ? + "" : "not "); + + acpi_os_printf ("\t\t_max address is %s fixed\n", + ADDRESS_FIXED == address32_data->max_address_fixed ? + "" : "not "); + + acpi_os_printf ("\t\t_granularity: 0x%08x\n", + address32_data->granularity); + + acpi_os_printf ("\t\t_address range min: 0x%08x\n", + address32_data->min_address_range); + + acpi_os_printf ("\t\t_address range max: 0x%08x\n", + address32_data->max_address_range); + + acpi_os_printf ("\t\t_address translation offset: 0x%08x\n", + address32_data->address_translation_offset); + + acpi_os_printf ("\t\t_address Length: 0x%08x\n", + address32_data->address_length); + + if(0xFF != address32_data->resource_source_index) { + acpi_os_printf ("\t\t_resource Source Index: %d\n", + address32_data->resource_source_index); + acpi_os_printf ("\t\t_resource Source: %s\n", + address32_data->resource_source); + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_extended_irq + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_extended_irq ( + RESOURCE_DATA *data) +{ + EXTENDED_IRQ_RESOURCE *ext_irq_data = (EXTENDED_IRQ_RESOURCE*) data; + u8 index = 0; + + + acpi_os_printf ("\t_extended IRQ Resource\n"); + + acpi_os_printf ("\t\t_resource %s\n", + CONSUMER == ext_irq_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf ("\t\t%s\n", + LEVEL_SENSITIVE == ext_irq_data->edge_level ? + "Level" : "Edge"); + + acpi_os_printf ("\t\t_active %s\n", + ACTIVE_LOW == ext_irq_data->active_high_low ? + "low" : "high"); + + acpi_os_printf ("\t\t%s\n", + SHARED == ext_irq_data->shared_exclusive ? + "Shared" : "Exclusive"); + + acpi_os_printf ("\t\t_interrupts : %d ( ", + ext_irq_data->number_of_interrupts); + + for (index = 0; index < ext_irq_data->number_of_interrupts; index++) { + acpi_os_printf ("%d ", ext_irq_data->interrupts[index]); + } + + acpi_os_printf (")\n"); + + if(0xFF != ext_irq_data->resource_source_index) { + acpi_os_printf ("\t\t_resource Source Index: %d", + ext_irq_data->resource_source_index); + acpi_os_printf ("\t\t_resource Source: %s", + ext_irq_data->resource_source); + } + + return; +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_resource_list + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Dispatches the structure to the correct dump routine. + * + ******************************************************************************/ + +void +acpi_rs_dump_resource_list ( + RESOURCE *resource) +{ + s8 count = 0; + u8 done = FALSE; + + + if (acpi_dbg_level & TRACE_RESOURCES && _COMPONENT & acpi_dbg_layer) { + while (!done) { + acpi_os_printf ("\t_resource structure %x.\n", count++); + + switch (resource->id) + { + case irq: + acpi_rs_dump_irq (&resource->data); + break; + + case dma: + acpi_rs_dump_dma (&resource->data); + break; + + case start_dependent_functions: + acpi_rs_dump_start_dependent_functions (&resource->data); + break; + + case end_dependent_functions: + acpi_os_printf ("\t_end_dependent_functions Resource\n"); + /* Acpi_rs_dump_end_dependent_functions (Resource->Data);*/ + break; + + case io: + acpi_rs_dump_io (&resource->data); + break; + + case fixed_io: + acpi_rs_dump_fixed_io (&resource->data); + break; + + case vendor_specific: + acpi_rs_dump_vendor_specific (&resource->data); + break; + + case end_tag: + /*Rs_dump_end_tag (Resource->Data);*/ + acpi_os_printf ("\t_end_tag Resource\n"); + done = TRUE; + break; + + case memory24: + acpi_rs_dump_memory24 (&resource->data); + break; + + case memory32: + acpi_rs_dump_memory32 (&resource->data); + break; + + case fixed_memory32: + acpi_rs_dump_fixed_memory32 (&resource->data); + break; + + case address16: + acpi_rs_dump_address16 (&resource->data); + break; + + case address32: + acpi_rs_dump_address32 (&resource->data); + break; + + case extended_irq: + acpi_rs_dump_extended_irq (&resource->data); + break; + + default: + acpi_os_printf ("Invalid resource type\n"); + break; + + } + + resource = (RESOURCE *) ((NATIVE_UINT) resource + + (NATIVE_UINT) resource->length); + } + } + + return; +} + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_dump_irq_list + * + * PARAMETERS: Data - pointer to the routing table to dump. + * + * RETURN: + * + * DESCRIPTION: Dispatches the structures to the correct dump routine. + * + ******************************************************************************/ + +void +acpi_rs_dump_irq_list ( + u8 *route_table) +{ + u8 *buffer = route_table; + s8 count = 0; + u8 done = FALSE; + PCI_ROUTING_TABLE *prt_element; + + + if (acpi_dbg_level & TRACE_RESOURCES && _COMPONENT & acpi_dbg_layer) { + prt_element = (PCI_ROUTING_TABLE *)buffer; + + while (!done) { + acpi_os_printf ("\t_pCI IRQ Routing Table structure %x.\n", count++); + + acpi_os_printf ("\t\t_address: 0x%x\n", + prt_element->data.address); + + acpi_os_printf ("\t\t_pin: 0x%x\n", prt_element->data.pin); + + acpi_os_printf ("\t\t_source: %s\n", prt_element->data.source); + + acpi_os_printf ("\t\t_source_index: 0x%x\n", + prt_element->data.source_index); + + buffer += prt_element->length; + + prt_element = (PCI_ROUTING_TABLE *)buffer; + + if(0 == prt_element->length) { + done = TRUE; + } + } + } + + return; +} + diff --git a/drivers/acpi/resources/rsio.c b/drivers/acpi/resources/rsio.c new file mode 100644 index 000000000..cc8e38c45 --- /dev/null +++ b/drivers/acpi/resources/rsio.c @@ -0,0 +1,544 @@ +/****************************************************************************** + * + * Module Name: rsio - Acpi_rs_io_resource + * Acpi_rs_fixed_io_resource + * Acpi_rs_io_stream + * Acpi_rs_fixed_io_stream + * Acpi_rs_dma_resource + * Acpi_rs_dma_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsio"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_io_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_io_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (IO_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 8; + + output_struct->id = io; + + /* + * Check Decode + */ + buffer += 1; + temp8 = *buffer; + + output_struct->data.io.io_decode = temp8 & 0x01; + + /* + * Check Min_base Address + */ + buffer += 1; + temp16 = *(u16 *)buffer; + + output_struct->data.io.min_base_address = temp16; + + /* + * Check Max_base Address + */ + buffer += 2; + temp16 = *(u16 *)buffer; + + output_struct->data.io.max_base_address = temp16; + + /* + * Check Base alignment + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.io.alignment = temp8; + + /* + * Check Range_length + */ + buffer += 1; + temp8 = *buffer; + + output_struct->data.io.range_length = temp8; + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_fixed_io_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_fixed_io_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (FIXED_IO_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 4; + + output_struct->id = fixed_io; + + /* + * Check Range Base Address + */ + buffer += 1; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + output_struct->data.fixed_io.base_address = temp16; + + /* + * Check Range_length + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.fixed_io.range_length = temp8; + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_io_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_io_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x47; + + buffer += 1; + + /* + * Io Information Byte + */ + temp8 = (u8) (linked_list->data.io.io_decode & 0x01); + + *buffer = temp8; + + buffer += 1; + + /* + * Set the Range minimum base address + */ + temp16 = (u16) linked_list->data.io.min_base_address; + + *(u16 *)buffer = temp16; + + buffer += 2; + + /* + * Set the Range maximum base address + */ + temp16 = (u16) linked_list->data.io.max_base_address; + + *(u16 *)buffer = temp16; + + buffer += 2; + + /* + * Set the base alignment + */ + temp8 = (u8) linked_list->data.io.alignment; + + *buffer = temp8; + + buffer += 1; + + /* + * Set the range length + */ + temp8 = (u8) linked_list->data.io.range_length; + + *buffer = temp8; + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_fixed_io_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_fixed_io_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x4B; + + buffer += 1; + + /* + * Set the Range base address + */ + temp16 = (u16) linked_list->data.fixed_io.base_address; + + *(u16 *)buffer = temp16; + + buffer += 2; + + /* + * Set the range length + */ + temp8 = (u8) linked_list->data.fixed_io.range_length; + + *buffer = temp8; + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_dma_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_dma_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u8 temp8 = 0; + u8 index; + u8 i; + u32 struct_size = sizeof(DMA_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 3; + + output_struct->id = dma; + + /* + * Point to the 8-bits of Byte 1 + */ + buffer += 1; + + temp8 = *buffer; + + /* Decode the IRQ bits */ + + for (i = 0, index = 0; index < 8; index++) { + if ((temp8 >> index) & 0x01) { + output_struct->data.dma.channels[i] = index; + i++; + } + } + output_struct->data.dma.number_of_channels = i; + + + /* + * Calculate the structure size based upon the number of interrupts + */ + struct_size += (output_struct->data.dma.number_of_channels - 1) * 4; + + /* + * Point to Byte 2 + */ + buffer += 1; + + temp8 = *buffer; + + /* + * Check for transfer preference (Bits[1:0]) + */ + output_struct->data.dma.transfer = temp8 & 0x03; + + if (0x03 == output_struct->data.dma.transfer) { + return (AE_BAD_DATA); + } + + /* + * Get bus master preference (Bit[2]) + */ + output_struct->data.dma.bus_master = (temp8 >> 2) & 0x01; + + /* + * Get channel speed support (Bits[6:5]) + */ + output_struct->data.dma.type = (temp8 >> 5) & 0x03; + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_dma_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_dma_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + + + /* + * The descriptor field is static + */ + *buffer = 0x2A; + + buffer += 1; + + temp8 = 0; + + /* + * Loop through all of the Channels and set the mask bits + */ + for (index = 0; + index < linked_list->data.dma.number_of_channels; + index++) + { + temp16 = (u16) linked_list->data.dma.channels[index]; + temp8 |= 0x1 << temp16; + } + + *buffer = temp8; + + buffer += 1; + + /* + * Set the DMA Info + */ + temp8 = (u8) ((linked_list->data.dma.type & 0x03) << 5); + + temp8 |= ((linked_list->data.dma.bus_master & 0x01) << 2); + + temp8 |= (linked_list->data.dma.transfer & 0x03); + + *buffer = temp8; + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + diff --git a/drivers/acpi/resources/rsirq.c b/drivers/acpi/resources/rsirq.c new file mode 100644 index 000000000..964d126e2 --- /dev/null +++ b/drivers/acpi/resources/rsirq.c @@ -0,0 +1,568 @@ +/****************************************************************************** + * + * Module Name: rsirq - Acpi_rs_irq_resource, + * Acpi_rs_irq_stream + * Acpi_rs_extended_irq_resource + * Acpi_rs_extended_irq_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsirq"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_irq_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_irq_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u8 i; + u32 struct_size = sizeof (IRQ_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * The number of bytes consumed are contained in the descriptor + * (Bits:0-1) + */ + temp8 = *buffer; + + *bytes_consumed = (temp8 & 0x03) + 1; + + output_struct->id = irq; + + /* + * Point to the 16-bits of Bytes 1 and 2 + */ + buffer += 1; + temp16 = *(u16 *)buffer; + + output_struct->data.irq.number_of_interrupts = 0; + + /* Decode the IRQ bits */ + for (i = 0, index = 0; index < 16; index++) { + if((temp16 >> index) & 0x01) { + output_struct->data.irq.interrupts[i] = index; + i++; + } + } + output_struct->data.irq.number_of_interrupts = i; + + /* + * Calculate the structure size based upon the number of interrupts + */ + struct_size += (output_struct->data.irq.number_of_interrupts - 1) * 4; + + /* + * Point to Byte 3 if it is used + */ + if (4 == *bytes_consumed) { + buffer += 2; + + temp8 = *buffer; + + /* + * Check for HE, LL or HL + */ + if (temp8 & 0x01) { + output_struct->data.irq.edge_level = EDGE_SENSITIVE; + output_struct->data.irq.active_high_low = ACTIVE_HIGH; + } + + else { + if (temp8 & 0x8) { + output_struct->data.irq.edge_level = LEVEL_SENSITIVE; + output_struct->data.irq.active_high_low = ACTIVE_LOW; + } + + else { + /* + * Only _LL and _HE polarity/trigger interrupts + * are allowed (ACPI spec v1.0b ection 6.4.2.1), + * so an error will occur if we reach this point + */ + return (AE_BAD_DATA); + } + } + + /* + * Check for sharable + */ + output_struct->data.irq.shared_exclusive = (temp8 >> 3) & 0x01; + } + + else { + /* + * Assume Edge Sensitive, Active High, Non-Sharable + * per ACPI Specification + */ + output_struct->data.irq.edge_level = EDGE_SENSITIVE; + output_struct->data.irq.active_high_low = ACTIVE_HIGH; + output_struct->data.irq.shared_exclusive = EXCLUSIVE; + } + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_irq_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_irq_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u8 IRQinfo_byte_needed; + + + /* + * The descriptor field is set based upon whether a third byte is + * needed to contain the IRQ Information. + */ + if (EDGE_SENSITIVE == linked_list->data.irq.edge_level && + ACTIVE_HIGH == linked_list->data.irq.active_high_low && + EXCLUSIVE == linked_list->data.irq.shared_exclusive) + { + *buffer = 0x22; + IRQinfo_byte_needed = FALSE; + } + else { + *buffer = 0x23; + IRQinfo_byte_needed = TRUE; + } + + buffer += 1; + + temp16 = 0; + + /* + * Loop through all of the interrupts and set the mask bits + */ + for(index = 0; + index < linked_list->data.irq.number_of_interrupts; + index++) + { + temp8 = (u8) linked_list->data.irq.interrupts[index]; + temp16 |= 0x1 << temp8; + } + + *(u16 *)buffer = temp16; + + buffer += 2; + + /* + * Set the IRQ Info byte if needed. + */ + if (IRQinfo_byte_needed) { + temp8 = 0; + + temp8 = (u8) ((linked_list->data.irq.shared_exclusive & + 0x01) << 4); + + if (LEVEL_SENSITIVE == linked_list->data.irq.edge_level && + ACTIVE_LOW == linked_list->data.irq.active_high_low) + { + temp8 |= 0x08; + } + + else { + temp8 |= 0x01; + } + + *buffer = temp8; + + buffer += 1; + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_extended_irq_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_extended_irq_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u32 struct_size = sizeof (EXTENDED_IRQ_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + temp16 = *(u16 *)buffer; + + *bytes_consumed = temp16 + 3; + output_struct->id = extended_irq; + + /* + * Point to the Byte3 + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.extended_irq.producer_consumer = temp8 & 0x01; + + /* + * Check for HE, LL or HL + */ + if(temp8 & 0x02) { + output_struct->data.extended_irq.edge_level = EDGE_SENSITIVE; + output_struct->data.extended_irq.active_high_low = ACTIVE_HIGH; + } + + else { + if(temp8 & 0x4) { + output_struct->data.extended_irq.edge_level = LEVEL_SENSITIVE; + output_struct->data.extended_irq.active_high_low = ACTIVE_LOW; + } + + else { + /* + * Only _LL and _HE polarity/trigger interrupts + * are allowed (ACPI spec v1.0b ection 6.4.2.1), + * so an error will occur if we reach this point + */ + return (AE_BAD_DATA); + } + } + + /* + * Check for sharable + */ + output_struct->data.extended_irq.shared_exclusive = + (temp8 >> 3) & 0x01; + + /* + * Point to Byte4 (IRQ Table length) + */ + buffer += 1; + temp8 = *buffer; + + output_struct->data.extended_irq.number_of_interrupts = temp8; + + /* + * Add any additional structure size to properly calculate + * the next pointer at the end of this function + */ + struct_size += (temp8 - 1) * 4; + + /* + * Point to Byte5 (First IRQ Number) + */ + buffer += 1; + + /* + * Cycle through every IRQ in the table + */ + for (index = 0; index < temp8; index++) { + output_struct->data.extended_irq.interrupts[index] = + (u32)*buffer; + + /* Point to the next IRQ */ + + buffer += 4; + } + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + */ + if (*bytes_consumed > + (u32)(output_struct->data.extended_irq.number_of_interrupts * + 4) + 5) + { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.extended_irq.resource_source_index = + (u32)temp8; + + /* Point to the String */ + + buffer += 1; + + /* Copy the string into the buffer */ + + index = 0; + + while (0x00 != *buffer) { + output_struct->data.extended_irq.resource_source[index] = + *buffer; + + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + output_struct->data.extended_irq.resource_source[index] = 0x00; + output_struct->data.extended_irq.resource_source_string_length = + index + 1; + + /* + * In order for the Struct_size to fall on a 32-bit boundry, + * calculate the length of the string and expand the + * Struct_size to the next 32-bit boundry. + */ + temp8 = (u8) (index + 1); + temp8 = (u8) ROUND_UP_TO_32_bITS (temp8); + } + + else { + output_struct->data.extended_irq.resource_source_index = 0x00; + output_struct->data.extended_irq.resource_source_string_length = 0; + output_struct->data.extended_irq.resource_source[0] = 0x00; + } + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_extended_irq_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_extended_irq_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 *length_field; + u8 temp8 = 0; + u8 index; + u8 *temp_pointer = NULL; + + + /* + * The descriptor field is static + */ + *buffer = 0x89; + buffer += 1; + + /* + * Set a pointer to the Length field - to be filled in later + */ + + length_field = (u16 *)buffer; + buffer += 2; + + /* + * Set the Interrupt vector flags + */ + temp8 = (u8)(linked_list->data.extended_irq.producer_consumer & 0x01); + + temp8 |= ((linked_list->data.extended_irq.shared_exclusive & 0x01) << 3); + + if (LEVEL_SENSITIVE == linked_list->data.extended_irq.edge_level && + ACTIVE_LOW == linked_list->data.extended_irq.active_high_low) + { + temp8 |= 0x04; + } + else { + temp8 |= 0x02; + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the Interrupt table length + */ + temp8 = (u8) linked_list->data.extended_irq.number_of_interrupts; + + *buffer = temp8; + + buffer += 1; + + for (index = 0; + index < linked_list->data.extended_irq.number_of_interrupts; + index++) + { + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.extended_irq.interrupts[index]); + buffer += 4; + } + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.extended_irq.resource_source_string_length) { + temp8 = (u8) linked_list->data.extended_irq.resource_source_index; + + *buffer = temp8; + buffer += 1; + temp_pointer = buffer; + + /* + * Copy the string + */ + STRCPY (temp_pointer, linked_list->data.extended_irq.resource_source); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (STRLEN (linked_list->data.extended_irq.resource_source) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + *length_field = (u16) (*bytes_consumed - 3); + + return (AE_OK); +} + diff --git a/drivers/acpi/resources/rslist.c b/drivers/acpi/resources/rslist.c new file mode 100644 index 000000000..3f96e5314 --- /dev/null +++ b/drivers/acpi/resources/rslist.c @@ -0,0 +1,506 @@ +/****************************************************************************** + * + * Module Name: rslist - Acpi_rs_byte_stream_to_list + * Acpi_list_to_byte_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "resource.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rslist"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_byte_stream_to_list + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource byte stream + * Byte_stream_buffer_length - Length of Byte_stream_buffer + * Output_buffer - Pointer to the buffer that will + * contain the output structures + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Takes the resource byte stream and parses it, creating a + * linked list of resources in the caller's output buffer + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_byte_stream_to_list ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + u8 **output_buffer) +{ + ACPI_STATUS status = AE_UNKNOWN_STATUS; + u32 bytes_parsed = 0; + u8 resource_type = 0; + u32 bytes_consumed = 0; + u8 **buffer = output_buffer; + u32 structure_size = 0; + u8 end_tag_processed = FALSE; + + + while (bytes_parsed < byte_stream_buffer_length && + FALSE == end_tag_processed) + { + /* + * Look at the next byte in the stream + */ + resource_type = *byte_stream_buffer; + + /* + * See if this is a small or large resource + */ + if(resource_type & 0x80) { + /* + * Large Resource Type + */ + switch (resource_type) + { + case MEMORY_RANGE_24: + /* + * 24-Bit Memory Resource + */ + status = acpi_rs_memory24_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case LARGE_VENDOR_DEFINED: + /* + * Vendor Defined Resource + */ + status = acpi_rs_vendor_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case MEMORY_RANGE_32: + /* + * 32-Bit Memory Range Resource + */ + status = acpi_rs_memory32_range_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case FIXED_MEMORY_RANGE_32: + /* + * 32-Bit Fixed Memory Resource + */ + status = acpi_rs_fixed_memory32_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case DWORD_ADDRESS_SPACE: + /* + * 32-Bit Address Resource + */ + status = acpi_rs_address32_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case WORD_ADDRESS_SPACE: + /* + * 16-Bit Address Resource + */ + status = acpi_rs_address16_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case EXTENDED_IRQ: + /* + * Extended IRQ + */ + status = acpi_rs_extended_irq_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + +/* 64-bit not currently supported */ +/* + case 0x8A: + break; +*/ + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_ERROR); + break; + } + } + + else { + /* + * Small Resource Type + * Only bits 7:3 are valid + */ + resource_type >>= 3; + + switch(resource_type) + { + case IRQ_FORMAT: + /* + * IRQ Resource + */ + status = acpi_rs_irq_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case DMA_FORMAT: + /* + * DMA Resource + */ + status = acpi_rs_dma_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case START_DEPENDENT_TAG: + /* + * Start Dependent Functions Resource + */ + status = acpi_rs_start_dependent_functions_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case END_DEPENDENT_TAG: + /* + * End Dependent Functions Resource + */ + status = acpi_rs_end_dependent_functions_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case IO_PORT_DESCRIPTOR: + /* + * IO Port Resource + */ + status = acpi_rs_io_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case FIXED_LOCATION_IO_DESCRIPTOR: + /* + * Fixed IO Port Resource + */ + status = acpi_rs_fixed_io_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case SMALL_VENDOR_DEFINED: + /* + * Vendor Specific Resource + */ + status = acpi_rs_vendor_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + + break; + + case END_TAG: + /* + * End Tag + */ + status = acpi_rs_end_tag_resource(byte_stream_buffer, + &bytes_consumed, + buffer, + &structure_size); + end_tag_processed = TRUE; + + break; + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_ERROR); + break; + + } /* switch */ + } /* if(Resource_type & 0x80) */ + + /* + * Update the return value and counter + */ + bytes_parsed += bytes_consumed; + + /* + * Set the byte stream to point to the next resource + */ + byte_stream_buffer += bytes_consumed; + + /* + * Set the Buffer to the next structure + */ + *buffer += structure_size; + + } /* while (Bytes_parsed < Byte_stream_buffer_length && + FALSE == End_tag_processed) */ + + /* + * Check the reason for exiting the while loop + */ + if (byte_stream_buffer_length != bytes_parsed || TRUE != end_tag_processed) { + return (AE_ERROR); + } + + return (AE_OK); + +} /* Acpi_rs_byte_stream_to_list */ + + +/*************************************************************************** + * FUNCTION: Acpi_rs_list_to_byte_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Byte_steam_size_needed - Calculated size of the byte stream + * needed from calling + * Acpi_rs_calculate_byte_stream_length() + * The size of the Output_buffer is + * guaranteed to be >= + * Byte_stream_size_needed + * Output_buffer - Pointer to the buffer that will + * contain the byte stream + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Takes the resource linked list and parses it, creating a + * byte stream of resources in the caller's output buffer + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_list_to_byte_stream ( + RESOURCE *linked_list, + u32 byte_stream_size_needed, + u8 **output_buffer) +{ + ACPI_STATUS status = AE_UNKNOWN_STATUS; + u8 *buffer = *output_buffer; + u32 bytes_consumed = 0; + u8 done = FALSE; + + + while (!done) { + switch (linked_list->id) + { + case irq: + /* + * IRQ Resource + */ + status = acpi_rs_irq_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case dma: + /* + * DMA Resource + */ + status = acpi_rs_dma_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case start_dependent_functions: + /* + * Start Dependent Functions Resource + */ + status = acpi_rs_start_dependent_functions_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case end_dependent_functions: + /* + * End Dependent Functions Resource + */ + status = acpi_rs_end_dependent_functions_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case io: + /* + * IO Port Resource + */ + status = acpi_rs_io_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case fixed_io: + /* + * Fixed IO Port Resource + */ + status = acpi_rs_fixed_io_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case vendor_specific: + /* + * Vendor Defined Resource + */ + status = acpi_rs_vendor_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case end_tag: + /* + * End Tag + */ + status = acpi_rs_end_tag_stream (linked_list, + &buffer, + &bytes_consumed); + + /* + * An End Tag indicates the end of the Resource Template + */ + done = TRUE; + break; + + case memory24: + /* + * 24-Bit Memory Resource + */ + status = acpi_rs_memory24_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case memory32: + /* + * 32-Bit Memory Range Resource + */ + status = acpi_rs_memory32_range_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case fixed_memory32: + /* + * 32-Bit Fixed Memory Resource + */ + status = acpi_rs_fixed_memory32_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case address16: + /* + * 16-Bit Address Descriptor Resource + */ + status = acpi_rs_address16_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case address32: + /* + * 32-Bit Address Descriptor Resource + */ + status = acpi_rs_address32_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + case extended_irq: + /* + * Extended IRQ Resource + */ + status = acpi_rs_extended_irq_stream (linked_list, + &buffer, + &bytes_consumed); + break; + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + return (AE_BAD_DATA); + break; + + } /* switch (Linked_list->Id) */ + + /* + * Set the Buffer to point to the open byte + */ + buffer += bytes_consumed; + + /* + * Point to the next object + */ + linked_list = (RESOURCE *) ((NATIVE_UINT) linked_list + + (NATIVE_UINT) linked_list->length); + } + + return (AE_OK); + +} /* Acpi_rs_list_to_byte_stream */ + diff --git a/drivers/acpi/resources/rsmemory.c b/drivers/acpi/resources/rsmemory.c new file mode 100644 index 000000000..2fdb40501 --- /dev/null +++ b/drivers/acpi/resources/rsmemory.c @@ -0,0 +1,570 @@ +/****************************************************************************** + * + * Module Name: rsmem24 - Acpi_rs_memory24_resource + * Acpi_rs_memory24_stream + * Acpi_rs_memory32_range_resource + * Acpi_rs_fixed_memory32_resource + * Acpi_rs_memory32_range_stream + * Acpi_rs_fixed_memory32_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsmemory"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_memory24_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_memory24_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (MEMORY24_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + + temp16 = *(u16 *)buffer; + + buffer += 2; + + *bytes_consumed = temp16 + 3; + + output_struct->id = memory24; + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + + buffer += 1; + + output_struct->data.memory24.read_write_attribute = temp8 & 0x01; + + /* + * Get Min_base_address (Bytes 4-5) + */ + temp16 = *(u16 *)buffer; + + buffer += 2; + + output_struct->data.memory24.min_base_address = temp16; + + /* + * Get Max_base_address (Bytes 6-7) + */ + temp16 = *(u16 *)buffer; + + buffer += 2; + + output_struct->data.memory24.max_base_address = temp16; + + /* + * Get Alignment (Bytes 8-9) + */ + temp16 = *(u16 *)buffer; + + buffer += 2; + + output_struct->data.memory24.alignment = temp16; + + /* + * Get Range_length (Bytes 10-11) + */ + temp16 = *(u16 *)buffer; + + output_struct->data.memory24.range_length = temp16; + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_memory24_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_memory24_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x81; + + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x09; + + MOVE_UNALIGNED16_TO_16 (buffer, &temp16); + + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.memory24.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range minimum base address + */ + MOVE_UNALIGNED16_TO_16 (buffer, &linked_list->data.memory24.min_base_address); + buffer += 2; + + /* + * Set the Range maximum base address + */ + MOVE_UNALIGNED16_TO_16 (buffer, &linked_list->data.memory24.max_base_address); + buffer += 2; + + /* + * Set the base alignment + */ + MOVE_UNALIGNED16_TO_16 (buffer, &linked_list->data.memory24.alignment); + buffer += 2; + + /* + * Set the range length + */ + MOVE_UNALIGNED16_TO_16 (buffer, &linked_list->data.memory24.range_length); + buffer += 2; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_memory32_range_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_memory32_range_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (MEMORY32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + buffer += 2; + *bytes_consumed = temp16 + 3; + + output_struct->id = memory32; + + /* + * Point to the place in the output buffer where the data portion will + * begin. + * 1. Set the RESOURCE_DATA * Data to point to it's own address, then + * 2. Set the pointer to the next address. + * + * NOTE: Output_struct->Data is cast to u8, otherwise, this addition adds + * 4 * sizeof(RESOURCE_DATA) instead of 4 * sizeof(u8) + */ + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + buffer += 1; + + output_struct->data.memory32.read_write_attribute = temp8 & 0x01; + + /* + * Get Min_base_address (Bytes 4-7) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.memory32.min_base_address, + buffer); + buffer += 4; + + /* + * Get Max_base_address (Bytes 8-11) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.memory32.max_base_address, + buffer); + buffer += 4; + + /* + * Get Alignment (Bytes 12-15) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.memory32.alignment, buffer); + buffer += 4; + + /* + * Get Range_length (Bytes 16-19) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.memory32.range_length, buffer); + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_fixed_memory32_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_fixed_memory32_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u32 struct_size = sizeof (FIXED_MEMORY32_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + MOVE_UNALIGNED16_TO_16 (&temp16, buffer); + + buffer += 2; + *bytes_consumed = temp16 + 3; + + output_struct->id = fixed_memory32; + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + buffer += 1; + output_struct->data.fixed_memory32.read_write_attribute = temp8 & 0x01; + + /* + * Get Range_base_address (Bytes 4-7) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.fixed_memory32.range_base_address, + buffer); + buffer += 4; + + /* + * Get Range_length (Bytes 8-11) + */ + MOVE_UNALIGNED32_TO_32 (&output_struct->data.fixed_memory32.range_length, + buffer); + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_memory32_range_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_memory32_range_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x85; + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x11; + + MOVE_UNALIGNED16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.memory32.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range minimum base address + */ + MOVE_UNALIGNED32_TO_32 (buffer, &linked_list->data.memory32.min_base_address); + buffer += 4; + + /* + * Set the Range maximum base address + */ + MOVE_UNALIGNED32_TO_32 (buffer, &linked_list->data.memory32.max_base_address); + buffer += 4; + + /* + * Set the base alignment + */ + MOVE_UNALIGNED32_TO_32 (buffer, &linked_list->data.memory32.alignment); + buffer += 4; + + /* + * Set the range length + */ + MOVE_UNALIGNED32_TO_32 (buffer, &linked_list->data.memory32.range_length); + buffer += 4; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_fixed_memory32_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_fixed_memory32_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x86; + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x09; + + MOVE_UNALIGNED16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.fixed_memory32.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range base address + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.fixed_memory32.range_base_address); + buffer += 4; + + /* + * Set the range length + */ + MOVE_UNALIGNED32_TO_32 (buffer, + &linked_list->data.fixed_memory32.range_length); + buffer += 4; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + diff --git a/drivers/acpi/resources/rsmisc.c b/drivers/acpi/resources/rsmisc.c new file mode 100644 index 000000000..fc5a6725f --- /dev/null +++ b/drivers/acpi/resources/rsmisc.c @@ -0,0 +1,612 @@ +/****************************************************************************** + * + * Module Name: rsmisc - Acpi_rs_end_tag_resource + * Acpi_rs_end_tag_stream + * Acpi_rs_vendor_resource + * Acpi_rs_vendor_stream + * Acpi_rs_start_dependent_functions_resource + * Acpi_rs_end_dependent_functions_resource + * Acpi_rs_start_dependent_functions_stream + * Acpi_rs_end_dependent_functions_stream + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsmisc"); + + +/*************************************************************************** + * FUNCTION: Acpi_rs_end_tag_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_end_tag_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u32 struct_size = RESOURCE_LENGTH; + + + /* + * The number of bytes consumed is static + */ + *bytes_consumed = 2; + + /* + * Fill out the structure + */ + output_struct->id = end_tag; + + /* + * Set the Length parameter + */ + output_struct->length = 0; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_end_tag_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_end_tag_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 temp8 = 0; + + + /* + * The descriptor field is static + */ + *buffer = 0x79; + + buffer += 1; + + /* + * Set the Checksum - zero means that the resource data is treated as if + * the checksum operation succeeded (ACPI Spec 1.0b Section 6.4.2.8) + */ + temp8 = 0; + + *buffer = temp8; + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + +/*************************************************************************** + * FUNCTION: Acpi_rs_vendor_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_vendor_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u32 struct_size = sizeof (VENDOR_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * Dereference the Descriptor to find if this is a large or small item. + */ + temp8 = *buffer; + + if (temp8 & 0x80) { + /* + * Large Item + */ + /* Point to the length field */ + + buffer += 1; + + /* Dereference */ + + temp16 = *(u16 *)buffer; + + /* Calculate bytes consumed */ + + *bytes_consumed = temp16 + 3; + + /* Point to the first vendor byte */ + + buffer += 2; + } + + else { + /* + * Small Item + */ + + /* Dereference the size */ + + temp16 = (u8)(*buffer & 0x07); + + /* Calculate bytes consumed */ + + *bytes_consumed = temp16 + 1; + + /* Point to the first vendor byte */ + + buffer += 1; + } + + output_struct->id = vendor_specific; + + output_struct->data.vendor_specific.length = temp16; + + for (index = 0; index < temp16; index++) { + output_struct->data.vendor_specific.reserved[index] = *buffer; + buffer += 1; + } + + /* + * In order for the Struct_size to fall on a 32-bit boundry, + * calculate the length of the vendor string and expand the + * Struct_size to the next 32-bit boundry. + */ + struct_size += ROUND_UP_TO_32_bITS (temp16); + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_vendor_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_vendor_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + + + /* + * Dereference the length to find if this is a large or small item. + */ + + if(linked_list->data.vendor_specific.length > 7) { + /* + * Large Item + */ + /* + * Set the descriptor field and length bytes + */ + *buffer = 0x84; + + buffer += 1; + + temp16 = (u16) linked_list->data.vendor_specific.length; + + *(u16 *)buffer = temp16; + + buffer += 2; + } + + else { + /* + * Small Item + */ + + /* + * Set the descriptor field + */ + temp8 = 0x70; + + temp8 |= linked_list->data.vendor_specific.length; + + *buffer = temp8; + + buffer += 1; + } + + /* + * Loop through all of the Vendor Specific fields + */ + for (index = 0; index < linked_list->data.vendor_specific.length; index++) { + temp8 = linked_list->data.vendor_specific.reserved[index]; + *buffer = temp8; + buffer += 1; + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + +/*************************************************************************** + * FUNCTION: Acpi_rs_start_dependent_functions_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_start_dependent_functions_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + u8 *buffer = byte_stream_buffer; + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u8 temp8 = 0; + u32 struct_size = + sizeof(START_DEPENDENT_FUNCTIONS_RESOURCE) + + RESOURCE_LENGTH_NO_DATA; + + + /* + * The number of bytes consumed are contained in the descriptor (Bits:0-1) + */ + temp8 = *buffer; + + *bytes_consumed = (temp8 & 0x01) + 1; + + output_struct->id = start_dependent_functions; + + /* + * Point to Byte 1 if it is used + */ + if (2 == *bytes_consumed) { + buffer += 1; + temp8 = *buffer; + + /* + * Check Compatibility priority + */ + output_struct->data.start_dependent_functions.compatibility_priority = + temp8 & 0x03; + + if (3 == output_struct->data.start_dependent_functions.compatibility_priority) { + return (AE_ERROR); + } + + /* + * Check Performance/Robustness preference + */ + output_struct->data.start_dependent_functions.performance_robustness = + (temp8 >> 2) & 0x03; + + if (3 == output_struct->data.start_dependent_functions.performance_robustness) { + return (AE_ERROR); + } + } + + else { + output_struct->data.start_dependent_functions.compatibility_priority = + ACCEPTABLE_CONFIGURATION; + + output_struct->data.start_dependent_functions.performance_robustness = + ACCEPTABLE_CONFIGURATION; + } + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_end_dependent_functions_resource + * + * PARAMETERS: + * Byte_stream_buffer - Pointer to the resource input byte + * stream + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes consumed from + * the Byte_stream_buffer + * Output_buffer - Pointer to the user's return buffer + * Structure_size - u32 pointer that is filled with + * the number of bytes in the filled + * in structure + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the Output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_end_dependent_functions_resource ( + u8 *byte_stream_buffer, + u32 *bytes_consumed, + u8 **output_buffer, + u32 *structure_size) +{ + RESOURCE *output_struct = (RESOURCE *) * output_buffer; + u32 struct_size = RESOURCE_LENGTH; + + + /* + * The number of bytes consumed is static + */ + *bytes_consumed = 1; + + /* + * Fill out the structure + */ + output_struct->id = end_dependent_functions; + + /* + * Set the Length parameter + */ + output_struct->length = struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_start_dependent_functions_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ +ACPI_STATUS +acpi_rs_start_dependent_functions_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 temp8 = 0; + + + /* + * The descriptor field is set based upon whether a byte is needed + * to contain Priority data. + */ + if (ACCEPTABLE_CONFIGURATION == + linked_list->data.start_dependent_functions.compatibility_priority && + ACCEPTABLE_CONFIGURATION == + linked_list->data.start_dependent_functions.performance_robustness) + { + *buffer = 0x30; + } + else { + *buffer = 0x31; + + buffer += 1; + + /* + * Set the Priority Byte Definition + */ + temp8 = 0; + + temp8 = (u8) + ((linked_list->data.start_dependent_functions.performance_robustness & + 0x03) << 2); + + temp8 |= + (linked_list->data.start_dependent_functions.compatibility_priority & + 0x03); + + *buffer = temp8; + } + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + + +/*************************************************************************** + * FUNCTION: Acpi_rs_end_dependent_functions_stream + * + * PARAMETERS: + * Linked_list - Pointer to the resource linked list + * Output_buffer - Pointer to the user's return buffer + * Bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * Output_buffer used + * + * RETURN: Status AE_OK if okay, else a valid ACPI_STATUS code + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ***************************************************************************/ + +ACPI_STATUS +acpi_rs_end_dependent_functions_stream ( + RESOURCE *linked_list, + u8 **output_buffer, + u32 *bytes_consumed + ) +{ + u8 *buffer = *output_buffer; + + + /* + * The descriptor field is static + */ + *buffer = 0x38; + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = (u32) ((NATIVE_UINT) buffer - + (NATIVE_UINT) *output_buffer); + + return (AE_OK); +} + diff --git a/drivers/acpi/resources/rsutils.c b/drivers/acpi/resources/rsutils.c new file mode 100644 index 000000000..cfd8949f3 --- /dev/null +++ b/drivers/acpi/resources/rsutils.c @@ -0,0 +1,430 @@ +/****************************************************************************** + * + * Module Name: rsutils - Utilities for the resource manager + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "namesp.h" +#include "resource.h" + + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsutils"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_get_prt_method_data + * + * PARAMETERS: Device_handle - a handle to the containing object + * Ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get the _PRT value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_rs_get_prt_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_OBJECT_INTERNAL *ret_obj; + ACPI_STATUS status; + u32 buffer_space_needed = ret_buffer->length; + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * a return buffer structure and if there is a non-zero buffer length + * we also need a valid pointer in the buffer + */ + if ((!handle) || + (!ret_buffer) || + ((!ret_buffer->pointer) && (ret_buffer->length))) + { + return (AE_BAD_PARAMETER); + } + + /* + * Execute the method, no parameters + */ + status = acpi_ns_evaluate_relative (handle, "_PRT", NULL, &ret_obj); + if (status != AE_OK) { + return (status); + } + + if (!ret_obj) { + /* Return object is required */ + + return (AE_TYPE); + } + + + /* + * The return object will be a package, so check the + * parameters. If the return object is not a package, + * then the underlying AML code is corrupt or improperly + * written. + */ + if (ACPI_TYPE_PACKAGE != ret_obj->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_pci_routing_table (ret_obj, + ret_buffer->pointer, + &buffer_space_needed); + + /* + * Tell the user how much of the buffer we have used or is needed + * and return the final status. + */ + ret_buffer->length = buffer_space_needed; + + + /* On exit, we must delete the object returned by evaluate_object */ + +cleanup: + + acpi_cm_remove_reference (ret_obj); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_get_crs_method_data + * + * PARAMETERS: Device_handle - a handle to the containing object + * Ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get the _CRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_rs_get_crs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_OBJECT_INTERNAL *ret_obj; + ACPI_STATUS status; + u32 buffer_space_needed = ret_buffer->length; + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * a return buffer structure and if there is a non-zero buffer length + * we also need a valid pointer in the buffer + */ + if ((!handle) || + (!ret_buffer) || + ((!ret_buffer->pointer) && (ret_buffer->length))) + { + return (AE_BAD_PARAMETER); + } + + /* + * Execute the method, no parameters + */ + status = acpi_ns_evaluate_relative (handle, "_CRS", NULL, &ret_obj); + if (status != AE_OK) { + return (status); + } + + if (!ret_obj) { + /* Return object is required */ + + return (AE_TYPE); + } + + /* + * The return object will be a buffer, but check the + * parameters. If the return object is not a buffer, + * then the underlying AML code is corrupt or improperly + * written. + */ + if (ACPI_TYPE_BUFFER != ret_obj->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list (ret_obj, + ret_buffer->pointer, + &buffer_space_needed); + + if (AE_OK == status) { + acpi_rs_dump_resource_list((RESOURCE *)ret_buffer->pointer); + } + + /* + * Tell the user how much of the buffer we have used or is needed + * and return the final status. + */ + ret_buffer->length = buffer_space_needed; + + + /* On exit, we must delete the object returned by evaluate_object */ + +cleanup: + + acpi_cm_remove_reference (ret_obj); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_get_prs_method_data + * + * PARAMETERS: Device_handle - a handle to the containing object + * Ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get the _PRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_rs_get_prs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_OBJECT_INTERNAL *ret_obj; + ACPI_STATUS status; + u32 buffer_space_needed = ret_buffer->length; + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * a return buffer structure and if there is a non-zero buffer length + * we also need a valid pointer in the buffer + */ + if ((!handle) || + (!ret_buffer) || + ((!ret_buffer->pointer) && (ret_buffer->length))) + { + return (AE_BAD_PARAMETER); + } + + /* + * Execute the method, no parameters + */ + status = acpi_ns_evaluate_relative (handle, "_PRS", NULL, &ret_obj); + if (status != AE_OK) { + return (status); + } + + if (!ret_obj) { + /* Return object is required */ + + return (AE_TYPE); + } + + /* + * The return object will be a buffer, but check the + * parameters. If the return object is not a buffer, + * then the underlying AML code is corrupt or improperly + * written.. + */ + if (ACPI_TYPE_BUFFER != ret_obj->common.type) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list (ret_obj, + ret_buffer->pointer, + &buffer_space_needed); + + /* + * Tell the user how much of the buffer we have used or is needed + * and return the final status. + */ + ret_buffer->length = buffer_space_needed; + + + /* On exit, we must delete the object returned by evaluate_object */ + +cleanup: + + acpi_cm_remove_reference (ret_obj); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_rs_set_srs_method_data + * + * PARAMETERS: Device_handle - a handle to the containing object + * *Method_name - Name of method to execute, If NULL, the + * handle is the object to execute + * In_buffer - a pointer to a buffer structure of the + * parameter + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to set the _SRS of an object contained + * in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_rs_set_srs_method_data ( + ACPI_HANDLE handle, + ACPI_BUFFER *in_buffer) +{ + ACPI_OBJECT_INTERNAL *params[2]; + ACPI_OBJECT_INTERNAL param_obj; + ACPI_STATUS status; + u8 *byte_stream = NULL; + u32 buffer_size_needed = 0; + + /* + * Must have a valid handle and buffer + */ + if ((!handle) || + (!in_buffer) || + (!in_buffer->pointer) || + (!in_buffer->length)) + { + return (AE_BAD_PARAMETER); + } + + /* + * The In_buffer parameter will point to a linked list of + * resource parameters. It needs to be formatted into a + * byte stream to be sent in as an input parameter. + */ + buffer_size_needed = 0; + + /* + * First call is to get the buffer size needed + */ + status = acpi_rs_create_byte_stream (in_buffer->pointer, + byte_stream, + &buffer_size_needed); + + /* + * We expect a return of AE_BUFFER_OVERFLOW + * if not, exit with the error + */ + if (AE_BUFFER_OVERFLOW != status) { + return (status); + } + + /* + * Allocate the buffer needed + */ + byte_stream = acpi_cm_callocate(buffer_size_needed); + + if (NULL == byte_stream) { + return (AE_NO_MEMORY); + } + + /* + * Now call to convert the linked list into a byte stream + */ + status = acpi_rs_create_byte_stream (in_buffer->pointer, + byte_stream, + &buffer_size_needed); + + if(AE_OK != status) { + /* + * Failed the call + */ + acpi_cm_free (byte_stream); + return (status); + } + + /* + * Init the param object + */ + acpi_cm_init_static_object (¶m_obj); + + /* + * Method requires one parameter. Set it up + */ + params [0] = ¶m_obj; + params [1] = NULL; + + /* + * Set up the parameter object + */ + param_obj.common.type = ACPI_TYPE_BUFFER; + param_obj.buffer.length = buffer_size_needed; + param_obj.buffer.pointer = byte_stream; + + /* + * Execute the method, no return value + */ + status = acpi_ns_evaluate_relative (handle, "_SRS", params, NULL); + + /* + * Clean up and return the status from Acpi_ns_evaluate_relative + */ + acpi_cm_free (byte_stream); + return (status); +} + diff --git a/drivers/acpi/resources/rsxface.c b/drivers/acpi/resources/rsxface.c new file mode 100644 index 000000000..230bcb9c5 --- /dev/null +++ b/drivers/acpi/resources/rsxface.c @@ -0,0 +1,177 @@ +/****************************************************************************** + * + * Module Name: rsxface - Public interfaces to the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "interp.h" +#include "namesp.h" +#include "resource.h" + +#define _COMPONENT RESOURCE_MANAGER + MODULE_NAME ("rsxface"); + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_irq_routing_table + * + * PARAMETERS: Device_handle - a handle to the Bus device we are querying + * Out_buffer - a pointer to a buffer to receive the + * current resources for the device + * Buffer_length - the number of bytes available in the buffer + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get the IRQ routing table for a + * specific bus. The caller must first acquire a handle for the + * desired bus. The routine table is placed in the buffer pointed + * to by the Out_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of Out_buffer is undefined. + * + * This function attempts to execute the _PRT method contained in + * the object indicated by the passed Device_handle. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_irq_routing_table ( + ACPI_HANDLE device_handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_STATUS status; + + + status = acpi_rs_get_prt_method_data (device_handle, ret_buffer); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_current_resources + * + * PARAMETERS: Device_handle - a handle to the device object for the + * device we are querying + * Out_buffer - a pointer to a buffer to receive the + * current resources for the device + * Buffer_length - the number of bytes available in the buffer + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is placed in the buffer + * pointed to by the Out_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of Out_buffer is undefined. + * + * This function attempts to execute the _CRS method contained in + * the object indicated by the passed Device_handle. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_current_resources ( + ACPI_HANDLE device_handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_STATUS status; + + + status = acpi_rs_get_crs_method_data (device_handle, ret_buffer); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_get_possible_resources + * + * PARAMETERS: Device_handle - a handle to the device object for the + * device we are querying + * Out_buffer - a pointer to a buffer to receive the + * resources for the device + * Buffer_length - the number of bytes available in the buffer + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get a list of the possible resources + * for a specific device. The caller must first acquire a handle + * for the desired device. The resource data is placed in the + * buffer pointed to by the Out_buffer variable. + * + * If the function fails an appropriate status will be returned + * and the value of Out_buffer is undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_possible_resources ( + ACPI_HANDLE device_handle, + ACPI_BUFFER *ret_buffer) +{ + ACPI_STATUS status; + + + status = acpi_rs_get_prs_method_data (device_handle, ret_buffer); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: Acpi_set_current_resources + * + * PARAMETERS: Device_handle - a handle to the device object for the + * device we are changing the resources of + * Out_buffer - a pointer to a buffer containing the + * resources to be set for the device + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to set the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is passed to the routine + * the buffer pointed to by the In_buffer variable. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_set_current_resources ( + ACPI_HANDLE device_handle, + ACPI_BUFFER *in_buffer) +{ + ACPI_STATUS status; + + + status = acpi_rs_set_srs_method_data (device_handle, in_buffer); + + return (status); +} diff --git a/drivers/acpi/tables/tbget.c b/drivers/acpi/tables/tbget.c new file mode 100644 index 000000000..255ca172d --- /dev/null +++ b/drivers/acpi/tables/tbget.c @@ -0,0 +1,324 @@ + +/****************************************************************************** + * + * Module Name: tbget - ACPI Table get* routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "tables.h" + + +#define _COMPONENT TABLE_MANAGER + MODULE_NAME ("tbget"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_get_table_ptr + * + * PARAMETERS: Table_type - one of the defined table types + * Instance - Which table of this type + * Table_ptr_loc - pointer to location to place the pointer for + * return + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the pointer to an ACPI table. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_get_table_ptr ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_TABLE_HEADER **table_ptr_loc) +{ + ACPI_TABLE_DESC *table_desc; + u32 i; + + + if (!acpi_gbl_DSDT) { + return (AE_NO_ACPI_TABLES); + } + + if (table_type > ACPI_TABLE_MAX) { + return (AE_BAD_PARAMETER); + } + + + /* + * For all table types (Single/Multiple), the first + * instance is always in the list head. + */ + + if (instance == 1) { + /* + * Just pluck the pointer out of the global table! + * Will be null if no table is present + */ + + *table_ptr_loc = acpi_gbl_acpi_tables[table_type].pointer; + return (AE_OK); + } + + + /* + * Check for instance out of range + */ + if (instance > acpi_gbl_acpi_tables[table_type].count) { + return (AE_NOT_EXIST); + } + + /* Walk the list to get the table */ + + table_desc = acpi_gbl_acpi_tables[table_type].next; + for (i = 1; i < acpi_gbl_acpi_tables[table_type].count; i++) { + table_desc = table_desc->next; + } + + /* We are now pointing to the requested table's descriptor */ + + *table_ptr_loc = table_desc->pointer; + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_get_table + * + * PARAMETERS: Physical_address - Physical address of table to retrieve + * *Buffer_ptr - If == NULL, read data from buffer + * rather than searching memory + * *Table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Maps the physical address of table into a logical address + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_get_table ( + void *physical_address, + char *buffer_ptr, + ACPI_TABLE_DESC *table_info) +{ + ACPI_TABLE_HEADER *table_header = NULL; + ACPI_TABLE_HEADER *full_table = NULL; + u32 size; + u8 allocation; + ACPI_STATUS status = AE_OK; + + + if (!table_info) { + return (AE_BAD_PARAMETER); + } + + + if (buffer_ptr) { + /* + * Getting data from a buffer, not BIOS tables + */ + + table_header = (ACPI_TABLE_HEADER *) buffer_ptr; + status = acpi_tb_validate_table_header (table_header); + if (ACPI_FAILURE (status)) { + /* Table failed verification, map all errors to BAD_DATA */ + + return (AE_BAD_DATA); + } + + /* Allocate buffer for the entire table */ + + full_table = acpi_cm_allocate (table_header->length); + if (!full_table) { + return (AE_NO_MEMORY); + } + + /* Copy the entire table (including header) to the local buffer */ + + size = (ACPI_SIZE) table_header->length; + MEMCPY (full_table, buffer_ptr, size); + + /* Save allocation type */ + + allocation = ACPI_MEM_ALLOCATED; + } + + + /* + * Not reading from a buffer, just map the table's physical memory + * into our address space. + */ + else { + size = SIZE_IN_HEADER; + + status = acpi_tb_map_acpi_table (physical_address, &size, + (void **) &full_table); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Save allocation type */ + + allocation = ACPI_MEM_MAPPED; + } + + + /* Return values */ + + table_info->pointer = full_table; + table_info->length = size; + table_info->allocation = allocation; + table_info->base_pointer = full_table; + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_get_all_tables + * + * PARAMETERS: Number_of_tables - Number of tables to get + * Table_ptr - Input buffer pointer, optional + * + * RETURN: Status + * + * DESCRIPTION: Load and validate all tables other than the RSDT. The RSDT must + * already be loaded and validated. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_get_all_tables ( + u32 number_of_tables, + char *table_ptr) +{ + ACPI_STATUS status = AE_OK; + u32 index; + ACPI_TABLE_DESC table_info; + + + /* + * Loop through all table pointers found in RSDT. + * This will NOT include the FACS and DSDT - we must get + * them after the loop + */ + + for (index = 0; index < number_of_tables; index++) { + /* Clear the Table_info each time */ + + MEMSET (&table_info, 0, sizeof (ACPI_TABLE_DESC)); + + /* Get the table via the RSDT */ + + status = acpi_tb_get_table ((void *) acpi_gbl_RSDT->table_offset_entry[index], + table_ptr, &table_info); + + /* Ignore a table that failed verification */ + + if (status == AE_BAD_DATA) { + continue; + } + + /* However, abort on serious errors */ + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Recognize and install the table */ + + status = acpi_tb_install_table (table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + /* + * Unrecognized or unsupported table, delete it and ignore the + * error. Just get as many tables as we can, later we will + * determine if there are enough tables to continue. + */ + + acpi_tb_delete_single_table (&table_info); + } + } + + + /* + * Get the minimum set of ACPI tables, namely: + * + * 1) FACP (via RSDT in loop above) + * 2) FACS + * 3) DSDT + * + */ + + + /* + * Get the FACS (must have the FACP first, from loop above) + * Acpi_tb_get_table_facs will fail if FACP pointer is not valid + */ + + status = acpi_tb_get_table_facs (table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Install the FACS */ + + status = acpi_tb_install_table (table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + + /* Get the DSDT (We know that the FACP if valid now) */ + + status = acpi_tb_get_table ((void *) acpi_gbl_FACP->dsdt, table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Install the DSDT */ + + status = acpi_tb_install_table (table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Dump the DSDT Header */ + + /* Dump the entire DSDT */ + + /* + * Initialize the capabilities flags. + * Assumes that platform supports ACPI_MODE since we have tables! + */ + + acpi_gbl_system_flags |= acpi_hw_get_mode_capabilities (); + + return (status); +} + diff --git a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/tables/tbinstal.c new file mode 100644 index 000000000..54e077f08 --- /dev/null +++ b/drivers/acpi/tables/tbinstal.c @@ -0,0 +1,533 @@ + +/****************************************************************************** + * + * Module Name: tbinstal - ACPI table installation and removal + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "tables.h" + + +#define _COMPONENT TABLE_MANAGER + MODULE_NAME ("tbinstal"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_install_table + * + * PARAMETERS: Table_ptr - Input buffer pointer, optional + * Table_info - Return value from Acpi_tb_get_table + * + * RETURN: Status + * + * DESCRIPTION: Load and validate all tables other than the RSDT. The RSDT must + * already be loaded and validated. + * Install the table into the global data structs. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_install_table ( + char *table_ptr, + ACPI_TABLE_DESC *table_info) +{ + ACPI_TABLE_TYPE table_type; + ACPI_TABLE_HEADER *table_header; + ACPI_STATUS status; + + + /* + * Check the table signature and make sure it is recognized + * Also checks the header checksum + */ + + status = acpi_tb_recognize_table (table_ptr, table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Table type is returned by Recognize_table */ + + table_type = table_info->type; + table_header = table_info->pointer; + + /* Lock tables while installing */ + + acpi_cm_acquire_mutex (ACPI_MTX_TABLES); + + /* Install the table into the global data structure */ + + status = acpi_tb_init_table_descriptor (table_info->type, table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + acpi_cm_release_mutex (ACPI_MTX_TABLES); + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_recognize_table + * + * PARAMETERS: Table_ptr - Input buffer pointer, optional + * Table_info - Return value from Acpi_tb_get_table + * + * RETURN: Status + * + * DESCRIPTION: Check a table signature for a match against known table types + * + * NOTE: All table pointers are validated as follows: + * 1) Table pointer must point to valid physical memory + * 2) Signature must be 4 ASCII chars, even if we don't recognize the + * name + * 3) Table must be readable for length specified in the header + * 4) Table checksum must be valid (with the exception of the FACS + * which has no checksum for some odd reason) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_recognize_table ( + char *table_ptr, + ACPI_TABLE_DESC *table_info) +{ + ACPI_TABLE_HEADER *table_header; + ACPI_STATUS status; + ACPI_TABLE_TYPE table_type = 0; + u32 i; + + + /* Ensure that we have a valid table pointer */ + + table_header = (ACPI_TABLE_HEADER *) table_info->pointer; + if (!table_header) { + return (AE_BAD_PARAMETER); + } + + /* + * Search for a signature match among the known table types + * Start at index one -> Skip the RSDP + */ + + status = AE_SUPPORT; + for (i = 1; i < NUM_ACPI_TABLES; i++) { + if (!STRNCMP (table_header->signature, + acpi_gbl_acpi_table_data[i].signature, + acpi_gbl_acpi_table_data[i].sig_length)) + { + /* + * Found a signature match, get the pertinent info from the + * Table_data structure + */ + + table_type = i; + status = acpi_gbl_acpi_table_data[i].status; + + break; + } + } + + /* Return the table type and length via the info struct */ + + table_info->type = (u8) table_type; + table_info->length = table_header->length; + + + /* + * Validate checksum for _most_ tables, + * even the ones whose signature we don't recognize + */ + + if (table_type != ACPI_TABLE_FACS) { + /* But don't abort if the checksum is wrong */ + /* TBD: [Future] make this a configuration option? */ + + acpi_tb_verify_table_checksum (table_header); + } + + /* + * An AE_SUPPORT means that the table was not recognized. + * We basically ignore this; just print a debug message + */ + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_init_table_descriptor + * + * PARAMETERS: Table_type - The type of the table + * Table_info - A table info struct + * + * RETURN: None. + * + * DESCRIPTION: Install a table into the global data structs. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_init_table_descriptor ( + ACPI_TABLE_TYPE table_type, + ACPI_TABLE_DESC *table_info) +{ + ACPI_TABLE_DESC *list_head; + ACPI_TABLE_DESC *table_desc; + + + /* + * Install the table into the global data structure + */ + + list_head = &acpi_gbl_acpi_tables[table_type]; + table_desc = list_head; + + + /* + * Two major types of tables: 1) Only one instance is allowed. This + * includes most ACPI tables such as the DSDT. 2) Multiple instances of + * the table are allowed. This includes SSDT and PSDTs. + */ + + if (acpi_gbl_acpi_table_data[table_type].flags == ACPI_TABLE_SINGLE) { + /* + * Only one table allowed, just update the list head + */ + + if (list_head->pointer) { + return (AE_EXIST); + } + + table_desc->count = 1; + } + + + else { + /* + * Multiple tables allowed for this table type, we must link + * the new table in to the list of tables of this type. + */ + + if (list_head->pointer) { + table_desc = acpi_cm_callocate (sizeof (ACPI_TABLE_DESC)); + if (!table_desc) { + return (AE_NO_MEMORY); + } + + list_head->count++; + + /* Update the original previous */ + + list_head->prev->next = table_desc; + + /* Update new entry */ + + table_desc->prev = list_head->prev; + table_desc->next = (ACPI_TABLE_DESC *) list_head; + + /* Update list head */ + + list_head->prev = table_desc; + } + + else { + table_desc->count = 1; + } + } + + + /* Common initialization of the table descriptor */ + + table_desc->pointer = table_info->pointer; + table_desc->base_pointer = table_info->base_pointer; + table_desc->length = table_info->length; + table_desc->allocation = table_info->allocation; + table_desc->aml_pointer = (u8 *) (table_desc->pointer + 1), + table_desc->aml_length = (u32) (table_desc->length - + (u32) sizeof (ACPI_TABLE_HEADER)); + table_desc->table_id = acpi_cm_allocate_owner_id (OWNER_TYPE_TABLE); + table_desc->loaded_into_namespace = FALSE; + + /* + * Set the appropriate global pointer (if there is one) to point to the + * newly installed table + */ + + if (acpi_gbl_acpi_table_data[table_type].global_ptr) { + *(acpi_gbl_acpi_table_data[table_type].global_ptr) = table_info->pointer; + } + + + /* Return Data */ + + table_info->table_id = table_desc->table_id; + table_info->installed_desc = table_desc; + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_delete_acpi_tables + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete all internal ACPI tables + * + ******************************************************************************/ + +void +acpi_tb_delete_acpi_tables (void) +{ + u32 i; + + + /* + * Free memory allocated for ACPI tables + * Memory can either be mapped or allocated + */ + + for (i = 0; i < ACPI_TABLE_MAX; i++) { + acpi_tb_delete_acpi_table (i); + } + +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_delete_acpi_table + * + * PARAMETERS: Type - The table type to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete an internal ACPI table + * Locks the ACPI table mutex + * + ******************************************************************************/ + +void +acpi_tb_delete_acpi_table ( + ACPI_TABLE_TYPE type) +{ + + if (type > ACPI_TABLE_MAX) { + return; + } + + + acpi_cm_acquire_mutex (ACPI_MTX_TABLES); + + /* Free the table */ + + acpi_tb_free_acpi_tables_of_type (&acpi_gbl_acpi_tables[type]); + + + /* Clear the appropriate "typed" global table pointer */ + + switch (type) + { + case ACPI_TABLE_RSDP: + acpi_gbl_RSDP = NULL; + break; + + case ACPI_TABLE_APIC: + acpi_gbl_APIC = NULL; + break; + + case ACPI_TABLE_DSDT: + acpi_gbl_DSDT = NULL; + break; + + case ACPI_TABLE_FACP: + acpi_gbl_FACP = NULL; + break; + + case ACPI_TABLE_FACS: + acpi_gbl_FACS = NULL; + break; + + case ACPI_TABLE_PSDT: + break; + + case ACPI_TABLE_RSDT: + acpi_gbl_RSDT = NULL; + break; + + case ACPI_TABLE_SSDT: + break; + + case ACPI_TABLE_SBST: + acpi_gbl_SBST = NULL; + + default: + break; + } + + acpi_cm_release_mutex (ACPI_MTX_TABLES); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_delete_single_table + * + * PARAMETERS: Table_info - A table info struct + * + * RETURN: None. + * + * DESCRIPTION: Free the memory associated with an internal ACPI table that + * is either installed or has never been installed. + * Table mutex should be locked. + * + ******************************************************************************/ + +ACPI_TABLE_DESC * +acpi_tb_delete_single_table ( + ACPI_TABLE_DESC *table_desc) +{ + ACPI_TABLE_DESC *next_desc; + + + if (!table_desc) { + return (NULL); + } + + + /* Unlink the descriptor */ + + if (table_desc->prev) { + table_desc->prev->next = table_desc->next; + } + + if (table_desc->next) { + table_desc->next->prev = table_desc->prev; + } + + + /* Free the memory allocated for the table itself */ + + if (table_desc->pointer) { + /* Valid table, determine type of memory allocation */ + + switch (table_desc->allocation) + { + + case ACPI_MEM_NOT_ALLOCATED: + + break; + + + case ACPI_MEM_ALLOCATED: + + acpi_cm_free (table_desc->base_pointer); + break; + + + case ACPI_MEM_MAPPED: + + acpi_os_unmap_memory (table_desc->base_pointer, table_desc->length); + break; + } + } + + + /* Free the table descriptor (Don't delete the list head, tho) */ + + + if ((table_desc->prev) == (table_desc->next)) { + + next_desc = NULL; + + /* Clear the list head */ + + table_desc->pointer = NULL; + table_desc->length = 0; + table_desc->count = 0; + + } + + else { + /* Free the table descriptor */ + + next_desc = table_desc->next; + acpi_cm_free (table_desc); + } + + + return (next_desc); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_free_acpi_tables_of_type + * + * PARAMETERS: Table_info - A table info struct + * + * RETURN: None. + * + * DESCRIPTION: Free the memory associated with an internal ACPI table + * Table mutex should be locked. + * + ******************************************************************************/ + +void +acpi_tb_free_acpi_tables_of_type ( + ACPI_TABLE_DESC *list_head) +{ + ACPI_TABLE_DESC *table_desc; + u32 count; + u32 i; + + + /* Get the head of the list */ + + table_desc = list_head; + count = list_head->count; + + /* + * Walk the entire list, deleting both the allocated tables + * and the table descriptors + */ + + for (i = 0; i < count; i++) { + table_desc = acpi_tb_delete_single_table (table_desc); + } + + return; +} + + diff --git a/drivers/acpi/tables/tbtable.c b/drivers/acpi/tables/tbtable.c new file mode 100644 index 000000000..a78a23acf --- /dev/null +++ b/drivers/acpi/tables/tbtable.c @@ -0,0 +1,388 @@ + +/****************************************************************************** + * + * Module Name: tbtable - ACPI tables: FACP, FACS, and RSDP utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "hardware.h" +#include "tables.h" + + +#define _COMPONENT TABLE_MANAGER + MODULE_NAME ("tbtable"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_get_table_rsdt + * + * PARAMETERS: Number_of_tables - Where the table count is placed + * Table_ptr - Input buffer pointer, optional + * + * RETURN: Status + * + * DESCRIPTION: Load and validate the RSDP (ptr) and RSDT (table) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_get_table_rsdt ( + u32 *number_of_tables) +{ + ACPI_STATUS status = AE_OK; + ACPI_TABLE_DESC table_info; + + + /* Get the RSDP */ + + status = acpi_tb_find_rsdp (&table_info); + if (ACPI_FAILURE (status)) { + REPORT_WARNING ("RSDP structure not found"); + return (AE_NO_ACPI_TABLES); + } + + /* Save the table pointers and allocation info */ + + status = acpi_tb_init_table_descriptor (ACPI_TABLE_RSDP, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + acpi_gbl_RSDP = (ROOT_SYSTEM_DESCRIPTOR_POINTER *) table_info.pointer; + + + /* + * RSDP structure was found; Now get the RSDT + */ + + status = acpi_tb_get_table ((void *) acpi_gbl_RSDP->rsdt_physical_address, NULL, + &table_info); + if (ACPI_FAILURE (status)) { + if (status == AE_BAD_SIGNATURE) { + /* Invalid RSDT signature */ + + REPORT_ERROR ("Invalid signature where RSDP indicates RSDT should be located"); + + } + } + + + /* Always delete the RSDP mapping */ + + acpi_tb_delete_acpi_table (ACPI_TABLE_RSDP); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Save the table pointers and allocation info */ + + status = acpi_tb_init_table_descriptor (ACPI_TABLE_RSDT, &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + acpi_gbl_RSDT = (ROOT_SYSTEM_DESCRIPTION_TABLE *) table_info.pointer; + + + /* Valid RSDT signature, verify the checksum */ + + status = acpi_tb_verify_table_checksum ((ACPI_TABLE_HEADER *) acpi_gbl_RSDT); + + /* Determine the number of tables pointed to by the RSDT */ + + *number_of_tables = (s32) DIV_4 (acpi_gbl_RSDT->header.length - + sizeof (ACPI_TABLE_HEADER)); + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_scan_memory_for_rsdp + * + * PARAMETERS: Start_address - Starting pointer for search + * Length - Maximum length to search + * + * RETURN: Pointer to the RSDP if found, otherwise NULL. + * + * DESCRIPTION: Search a block of memory for the RSDP signature + * + ******************************************************************************/ + +char * +acpi_tb_scan_memory_for_rsdp ( + char *start_address, + u32 length) +{ + u32 offset; + char *mem_rover; + + + /* Search from given start addr for the requested length */ + + for (offset = 0, mem_rover = start_address; + offset < length; + offset += RSDP_SCAN_STEP, mem_rover += RSDP_SCAN_STEP) + { + + /* The signature and checksum must both be correct */ + + if (STRNCMP (mem_rover, RSDP_SIG, sizeof (RSDP_SIG)-1) == 0 && + acpi_tb_checksum (mem_rover, + sizeof (ROOT_SYSTEM_DESCRIPTOR_POINTER)) == 0) + { + /* If so, we have found the RSDP */ + + return mem_rover; + } + } + + /* Searched entire block, no RSDP was found */ + + return NULL; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_find_rsdp + * + * PARAMETERS: *Buffer_ptr - If == NULL, read data from buffer + * rather than searching memory + * *Table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Search lower 1_mbyte of memory for the root system descriptor + * pointer structure. If it is found, set *RSDP to point to it. + * + * NOTE: The RSDP must be either in the first 1_k of the Extended + * BIOS Data Area or between E0000 and FFFFF (ACPI 1.0 section + * 5.2.2; assertion #421). + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_find_rsdp ( + ACPI_TABLE_DESC *table_info) +{ + char *table_ptr; + char *mem_rover; + ACPI_STATUS status = AE_OK; + + if (acpi_gbl_acpi_init_data.RSDP_physical_address) { + /* + * RSDP address was supplied as part of the initialization data + */ + + status = acpi_os_map_memory(acpi_gbl_acpi_init_data.RSDP_physical_address, + sizeof (ROOT_SYSTEM_DESCRIPTOR_POINTER), + (void **)&table_ptr); + + if (ACPI_FAILURE (status)) { + return (status); + } + + if (!table_ptr) { + return (AE_NO_MEMORY); + } + + /* + * The signature and checksum must both be correct + */ + + if (STRNCMP (table_ptr, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { + /* Nope, BAD Signature */ + + return (AE_BAD_SIGNATURE); + } + + /* The signature and checksum must both be correct */ + + if (acpi_tb_checksum (table_ptr, + sizeof (ROOT_SYSTEM_DESCRIPTOR_POINTER)) != 0) + { + /* Nope, BAD Checksum */ + + return (AE_BAD_CHECKSUM); + } + + /* RSDP supplied is OK */ + /* If so, we have found the RSDP */ + + table_info->pointer = (ACPI_TABLE_HEADER *) table_ptr; + table_info->length = sizeof (ROOT_SYSTEM_DESCRIPTOR_POINTER); + table_info->allocation = ACPI_MEM_MAPPED; + table_info->base_pointer = table_ptr; + + return (AE_OK); + } + + /* + * Search memory for RSDP. First map low physical memory. + */ + + status = acpi_os_map_memory (LO_RSDP_WINDOW_BASE, LO_RSDP_WINDOW_SIZE, + (void **)&table_ptr); + + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * 1) Search EBDA (low memory) paragraphs + */ + + if (NULL != (mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, + LO_RSDP_WINDOW_SIZE))) + { + /* Found it, return pointer and don't delete the mapping */ + + table_info->pointer = (ACPI_TABLE_HEADER *) mem_rover; + table_info->length = LO_RSDP_WINDOW_SIZE; + table_info->allocation = ACPI_MEM_MAPPED; + table_info->base_pointer = table_ptr; + + return (AE_OK); + } + + /* This mapping is no longer needed */ + + acpi_os_unmap_memory (table_ptr, LO_RSDP_WINDOW_SIZE); + + + /* + * 2) Search upper memory: 16-byte boundaries in E0000h-F0000h + */ + + status = acpi_os_map_memory (HI_RSDP_WINDOW_BASE, HI_RSDP_WINDOW_SIZE, + (void **)&table_ptr); + + if (ACPI_FAILURE (status)) { + return (status); + } + + if (NULL != (mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, + HI_RSDP_WINDOW_SIZE))) + { + /* Found it, return pointer and don't delete the mapping */ + + table_info->pointer = (ACPI_TABLE_HEADER *) mem_rover; + table_info->length = HI_RSDP_WINDOW_SIZE; + table_info->allocation = ACPI_MEM_MAPPED; + table_info->base_pointer = table_ptr; + + return (AE_OK); + } + + /* This mapping is no longer needed */ + + acpi_os_unmap_memory (table_ptr, HI_RSDP_WINDOW_SIZE); + + + /* RSDP signature was not found */ + + return (AE_NOT_FOUND); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_get_table_facs + * + * PARAMETERS: *Buffer_ptr - If == NULL, read data from buffer + * rather than searching memory + * *Table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Returns a pointer to the FACS as defined in FACP. This + * function assumes the global variable FACP has been + * correctly initialized. The value of FACP->Firmware_ctrl + * into a far pointer which is returned. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_get_table_facs ( + char *buffer_ptr, + ACPI_TABLE_DESC *table_info) +{ + void *table_ptr = NULL; + u32 size; + u8 allocation; + ACPI_STATUS status = AE_OK; + + + /* Must have a valid FACP pointer */ + + if (!acpi_gbl_FACP) { + return (AE_NO_ACPI_TABLES); + } + + size = sizeof (FIRMWARE_ACPI_CONTROL_STRUCTURE); + if (buffer_ptr) { + /* + * Getting table from a file -- allocate a buffer and + * read the table. + */ + table_ptr = acpi_cm_allocate (size); + if(!table_ptr) { + return (AE_NO_MEMORY); + } + + MEMCPY (table_ptr, buffer_ptr, size); + + /* Save allocation type */ + + allocation = ACPI_MEM_ALLOCATED; + } + + else { + /* Just map the physical memory to our address space */ + + status = acpi_tb_map_acpi_table ((void *) acpi_gbl_FACP->firmware_ctrl, + &size, &table_ptr); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Save allocation type */ + + allocation = ACPI_MEM_MAPPED; + } + + + /* Return values */ + + table_info->pointer = table_ptr; + table_info->length = size; + table_info->allocation = allocation; + table_info->base_pointer = table_ptr; + + return (status); +} + diff --git a/drivers/acpi/tables/tbutils.c b/drivers/acpi/tables/tbutils.c new file mode 100644 index 000000000..45519a824 --- /dev/null +++ b/drivers/acpi/tables/tbutils.c @@ -0,0 +1,352 @@ + +/****************************************************************************** + * + * Module Name: tbutils - Table manipulation utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "tables.h" +#include "interp.h" + + +#define _COMPONENT TABLE_MANAGER + MODULE_NAME ("tbutils"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_system_table_pointer + * + * PARAMETERS: *Where - Pointer to be examined + * + * RETURN: TRUE if Where is within the AML stream (in one of the ACPI + * system tables such as the DSDT or an SSDT.) + * FALSE otherwise + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_handle_to_object ( + u16 table_id, + ACPI_TABLE_DESC **table_desc) +{ + u32 i; + ACPI_TABLE_DESC *list_head; + + + for (i = 0; i < ACPI_TABLE_MAX; i++) { + list_head = &acpi_gbl_acpi_tables[i]; + do + { + if (list_head->table_id == table_id) { + *table_desc = list_head; + return AE_OK; + } + + list_head = list_head->next; + + } while (list_head != &acpi_gbl_acpi_tables[i]); + } + + + return AE_BAD_PARAMETER; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_system_table_pointer + * + * PARAMETERS: *Where - Pointer to be examined + * + * RETURN: TRUE if Where is within the AML stream (in one of the ACPI + * system tables such as the DSDT or an SSDT.) + * FALSE otherwise + * + ******************************************************************************/ + +u8 +acpi_tb_system_table_pointer ( + void *where) +{ + u32 i; + ACPI_TABLE_DESC *table_desc; + ACPI_TABLE_HEADER *table; + + + /* No function trace, called too often! */ + + + /* Ignore null pointer */ + + if (!where) { + return (FALSE); + } + + + /* Check for a pointer within the DSDT */ + + if (IS_IN_ACPI_TABLE (where, acpi_gbl_DSDT)) { + return (TRUE); + } + + + /* Check each of the loaded SSDTs (if any)*/ + + table_desc = &acpi_gbl_acpi_tables[ACPI_TABLE_SSDT]; + + for (i = 0; i < acpi_gbl_acpi_tables[ACPI_TABLE_SSDT].count; i++) { + table = table_desc->pointer; + + if (IS_IN_ACPI_TABLE (where, table)) { + return (TRUE); + } + + table_desc = table_desc->next; + } + + + /* Check each of the loaded PSDTs (if any)*/ + + table_desc = &acpi_gbl_acpi_tables[ACPI_TABLE_PSDT]; + + for (i = 0; i < acpi_gbl_acpi_tables[ACPI_TABLE_PSDT].count; i++) { + table = table_desc->pointer; + + if (IS_IN_ACPI_TABLE (where, table)) { + return (TRUE); + } + + table_desc = table_desc->next; + } + + + /* Pointer does not point into any system table */ + + return (FALSE); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_validate_table_header + * + * PARAMETERS: Table_header - Logical pointer to the table + * + * RETURN: Status + * + * DESCRIPTION: Check an ACPI table header for validity + * + * NOTE: Table pointers are validated as follows: + * 1) Table pointer must point to valid physical memory + * 2) Signature must be 4 ASCII chars, even if we don't recognize the + * name + * 3) Table must be readable for length specified in the header + * 4) Table checksum must be valid (with the exception of the FACS + * which has no checksum for some odd reason) + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_validate_table_header ( + ACPI_TABLE_HEADER *table_header) +{ + ACPI_NAME signature; + + + /* Verify that this is a valid address */ + + if (!acpi_os_readable (table_header, sizeof (ACPI_TABLE_HEADER))) { + return AE_BAD_ADDRESS; + } + + + /* Ensure that the signature is 4 ASCII characters */ + + MOVE_UNALIGNED32_TO_32 (&signature, &table_header->signature); + if (!acpi_cm_valid_acpi_name (signature)) { + REPORT_WARNING ("Invalid table signature found"); + return AE_BAD_SIGNATURE; + } + + + /* Validate the table length */ + + if (table_header->length < sizeof (ACPI_TABLE_HEADER)) { + REPORT_WARNING ("Invalid table header length found"); + return AE_BAD_HEADER; + } + + return AE_OK; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_map_acpi_table + * + * PARAMETERS: Physical_address - Physical address of table to map + * *Size - Size of the table. If zero, the size + * from the table header is used. + * Actual size is returned here. + * **Logical_address - Logical address of mapped table + * + * RETURN: Logical address of the mapped table. + * + * DESCRIPTION: Maps the physical address of table into a logical address + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_map_acpi_table ( + void *physical_address, + u32 *size, + void **logical_address) +{ + ACPI_TABLE_HEADER *table; + u32 table_size = *size; + ACPI_STATUS status = AE_OK; + + + /* If size is zero, look at the table header to get the actual size */ + + if ((*size) == 0) { + /* Get the table header so we can extract the table length */ + + status = acpi_os_map_memory (physical_address, sizeof (ACPI_TABLE_HEADER), + (void **) &table); + if (ACPI_FAILURE (status)) { + return status; + } + + /* Extract the full table length before we delete the mapping */ + + table_size = table->length; + + /* + * Validate the header and delete the mapping. + * We will create a mapping for the full table below. + */ + + status = acpi_tb_validate_table_header (table); + + /* Always unmap the memory for the header */ + + acpi_os_unmap_memory (table, sizeof (ACPI_TABLE_HEADER)); + + /* Exit if header invalid */ + + if (ACPI_FAILURE (status)) { + return status; + } + } + + + /* Map the physical memory for the correct length */ + + status = acpi_os_map_memory (physical_address, table_size, (void **) &table); + if (ACPI_FAILURE (status)) { + return status; + } + + *size = table_size; + *logical_address = table; + + return status; +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_verify_table_checksum + * + * PARAMETERS: *Table_header - ACPI table to verify + * + * RETURN: 8 bit checksum of table + * + * DESCRIPTION: Does an 8 bit checksum of table and returns status. A correct + * table should have a checksum of 0. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_tb_verify_table_checksum ( + ACPI_TABLE_HEADER *table_header) +{ + u8 check_sum; + ACPI_STATUS status = AE_OK; + + + /* Compute the checksum on the table */ + + check_sum = acpi_tb_checksum (table_header, table_header->length); + + /* Return the appropriate exception */ + + if (check_sum) { + REPORT_ERROR ("Invalid ACPI table checksum"); + status = AE_BAD_CHECKSUM; + } + + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_tb_checksum + * + * PARAMETERS: Buffer - Buffer to checksum + * Length - Size of the buffer + * + * RETURNS 8 bit checksum of buffer + * + * DESCRIPTION: Computes an 8 bit checksum of the buffer(length) and returns it. + * + ******************************************************************************/ + +u8 +acpi_tb_checksum ( + void *buffer, + u32 length) +{ + u8 *limit; + u8 *rover; + u8 sum = 0; + + + if (buffer && length) { + /* Buffer and Length are valid */ + + limit = (u8 *) buffer + length; + + for (rover = buffer; rover < limit; rover++) { + sum = (u8) (sum + *rover); + } + } + + return sum; +} + + diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c new file mode 100644 index 000000000..ba650c114 --- /dev/null +++ b/drivers/acpi/tables/tbxface.c @@ -0,0 +1,352 @@ + +/****************************************************************************** + * + * Module Name: tbxface - Public interfaces to the ACPI subsystem + * ACPI table oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 R. Byron Moore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "acpi.h" +#include "namesp.h" +#include "interp.h" +#include "tables.h" + + +#define _COMPONENT TABLE_MANAGER + MODULE_NAME ("tbxface"); + + +/******************************************************************************* + * + * FUNCTION: Acpi_load_firmware_tables + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load the ACPI tables from BIOS + * + ******************************************************************************/ + +ACPI_STATUS +acpi_load_firmware_tables (void) +{ + ACPI_STATUS status = AE_OK; + u32 number_of_tables = 0; + + + /* Get the RSDT first */ + + status = acpi_tb_get_table_rsdt (&number_of_tables); + if (status != AE_OK) { + goto error_exit; + } + + + /* Now get the rest of the tables */ + + status = acpi_tb_get_all_tables (number_of_tables, NULL); + if (status != AE_OK) { + goto error_exit; + } + + + return (AE_OK); + + +error_exit: + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_load_table + * + * PARAMETERS: Table_ptr - pointer to a buffer containing the entire + * table to be loaded + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load a table from the caller's + * buffer. The buffer must contain an entire ACPI Table including + * a valid header. The header fields will be verified, and if it + * is determined that the table is invalid, the call will fail. + * + * If the call fails an appropriate status will be returned. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_load_table ( + ACPI_TABLE_HEADER *table_ptr) +{ + ACPI_STATUS status; + ACPI_TABLE_DESC table_info; + + + if (!table_ptr) { + return AE_BAD_PARAMETER; + } + + /* Copy the table to a local buffer */ + + status = acpi_tb_get_table (NULL, ((char *) table_ptr), &table_info); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Install the new table into the local data structures */ + + status = acpi_tb_install_table (NULL, &table_info); + if (ACPI_FAILURE (status)) { + /* TBD: [Errors] must free table allocated by Acpi_tb_get_table */ + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_unload_table + * + * PARAMETERS: Table_type - Type of table to be unloaded + * + * RETURN: Status + * + * DESCRIPTION: This routine is used to force the unload of a table + * + ******************************************************************************/ + +ACPI_STATUS +acpi_unload_table ( + ACPI_TABLE_TYPE table_type) +{ + ACPI_TABLE_DESC *list_head; + + + /* Parameter validation */ + + if (table_type > ACPI_TABLE_MAX) { + return (AE_BAD_PARAMETER); + } + + + /* Find all tables of the requested type */ + + list_head = &acpi_gbl_acpi_tables[table_type]; + do + { + /* Delete the entire namespace under this table NTE */ + + acpi_ns_delete_namespace_by_owner (list_head->table_id); + + /* Delete (or unmap) the actual table */ + + acpi_tb_delete_acpi_table (table_type); + + } while (list_head != &acpi_gbl_acpi_tables[table_type]); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_get_table_header + * + * PARAMETERS: Table_type - one of the defined table types + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * see Acpi_gbl_Acpi_table_flag + * Out_table_header - pointer to the ACPI_TABLE_HEADER if successful + * + * DESCRIPTION: This function is called to get an ACPI table header. The caller + * supplies an pointer to a data area sufficient to contain an ACPI + * ACPI_TABLE_HEADER structure. + * + * The header contains a length field that can be used to determine + * the size of the buffer needed to contain the entire table. This + * function is not valid for the RSD PTR table since it does not + * have a standard header and is fixed length. + * + * If the operation fails for any reason an appropriate status will + * be returned and the contents of Out_table_header are undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_table_header ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_TABLE_HEADER *out_table_header) +{ + ACPI_TABLE_HEADER *tbl_ptr; + ACPI_STATUS status; + + + status = AE_OK; + + if ((instance == 0) || + (table_type == ACPI_TABLE_RSDP) || + (!out_table_header)) + { + return (AE_BAD_PARAMETER); + } + + /* Check the table type and instance */ + + if ((table_type > ACPI_TABLE_MAX) || + (acpi_gbl_acpi_table_data[table_type].flags == ACPI_TABLE_SINGLE && + instance > 1)) + { + return (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the entire table */ + + status = acpi_tb_get_table_ptr (table_type, instance, &tbl_ptr); + if (status != AE_OK) { + return (status); + } + + /* + * The function will return a NULL pointer if the table is not loaded + */ + if (tbl_ptr == NULL) { + return (AE_NOT_EXIST); + } + + /* + * Copy the header to the caller's buffer + */ + MEMCPY ((void *) out_table_header, (void *) tbl_ptr, + sizeof (ACPI_TABLE_HEADER)); + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: Acpi_get_table + * + * PARAMETERS: Table_type - one of the defined table types + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * see Acpi_gbl_Acpi_table_flag + * Ret_buffer - pointer to a structure containing a buffer to + * receive the table + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get an ACPI table. The caller + * supplies an Out_buffer large enough to contain the entire ACPI + * table. The caller should call the Acpi_get_table_header function + * first to determine the buffer size needed. Upon completion + * the Out_buffer->Length field will indicate the number of bytes + * copied into the Out_buffer->Buf_ptr buffer. This table will be + * a complete table including the header. + * + * If the operation fails an appropriate status will be returned + * and the contents of Out_buffer are undefined. + * + ******************************************************************************/ + +ACPI_STATUS +acpi_get_table ( + ACPI_TABLE_TYPE table_type, + u32 instance, + ACPI_BUFFER *ret_buffer) +{ + ACPI_TABLE_HEADER *tbl_ptr; + ACPI_STATUS status; + u32 ret_buf_len; + + + status = AE_OK; + + /* + * Must have a buffer + */ + if ((instance == 0) || + (!ret_buffer) || + (!ret_buffer->pointer) || + (!ret_buffer->length)) + { + return (AE_BAD_PARAMETER); + } + + /* Check the table type and instance */ + + if ((table_type > ACPI_TABLE_MAX) || + (acpi_gbl_acpi_table_data[table_type].flags == ACPI_TABLE_SINGLE && + instance > 1)) + { + return (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the entire table */ + + status = acpi_tb_get_table_ptr (table_type, instance, &tbl_ptr); + if (status != AE_OK) { + return (status); + } + + /* + * The function will return a NULL pointer if the table is not loaded + */ + if (tbl_ptr == NULL) { + return (AE_NOT_EXIST); + } + + /* + * Got a table ptr, assume it's ok and copy it to the user's buffer + */ + if (table_type == ACPI_TABLE_RSDP) { + /* + * RSD PTR is the only "table" without a header + */ + ret_buf_len = sizeof (ROOT_SYSTEM_DESCRIPTOR_POINTER); + } + else { + ret_buf_len = tbl_ptr->length; + } + + /* + * Verify we have space in the caller's buffer for the table + */ + if (ret_buffer->length < ret_buf_len) { + ret_buffer->length = ret_buf_len; + return (AE_BUFFER_OVERFLOW); + } + + ret_buffer->length = ret_buf_len; + + MEMCPY ((void *) ret_buffer->pointer, (void *) tbl_ptr, ret_buf_len); + + return (AE_OK); +} + diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c index 62758a297..785a0e551 100644 --- a/drivers/block/acsi_slm.c +++ b/drivers/block/acsi_slm.c @@ -1007,7 +1007,7 @@ int slm_init( void ) BufferP = SLMBuffer; SLMState = IDLE; - devfs_handle = devfs_mk_dir (NULL, "slm", 3, NULL); + devfs_handle = devfs_mk_dir (NULL, "slm", NULL); devfs_register_series (devfs_handle, "%u", MAX_SLM, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &slm_fops, NULL); diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index b7aa4241e..33dd5b896 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -355,7 +355,7 @@ static void fd_select_side( int side ); static void fd_select_drive( int drive ); static void fd_deselect( void ); static void fd_motor_off_timer( unsigned long dummy ); -static void check_change( void ); +static void check_change( unsigned long dummy ); static __inline__ void set_head_settle_flag( void ); static __inline__ int get_head_settle_flag( void ); static void floppy_irq (int irq, void *dummy, struct pt_regs *fp); @@ -391,14 +391,16 @@ static int floppy_release( struct inode * inode, struct file * filp ); /************************* End of Prototypes **************************/ static struct timer_list motor_off_timer = - { NULL, NULL, 0, 0, fd_motor_off_timer }; + { function: fd_motor_off_timer }; static struct timer_list readtrack_timer = - { NULL, NULL, 0, 0, fd_readtrack_check }; + { function: fd_readtrack_check }; static struct timer_list timeout_timer = - { NULL, NULL, 0, 0, fd_times_out }; - + { function: fd_times_out }; +static struct timer_list fd_timer = + { function: check_change }; + static inline void start_motor_off_timer(void) { @@ -407,10 +409,9 @@ start_motor_off_timer(void) } static inline void -start_check_change_timer(void) +start_check_change_timer(unsigned long dummy) { - timer_table[FLOPPY_TIMER].expires = jiffies + CHECK_CHANGE_DELAY; - timer_active |= (1 << FLOPPY_TIMER); + mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY); } static inline void @@ -1309,12 +1310,11 @@ static void finish_fdc_done( int dummy ) stop_timeout(); NeedSeek = 0; - if ((timer_active & (1 << FLOPPY_TIMER)) && - time_before(timer_table[FLOPPY_TIMER].expires, jiffies + 5)) + if (timer_pending(&fd_timer) && time_before(fd_timer.expires, jiffies + 5)) /* If the check for a disk change is done too early after this * last seek command, the WP bit still reads wrong :-(( */ - timer_table[FLOPPY_TIMER].expires = jiffies + 5; + mod_timer(&fd_timer, jiffies + 5); else start_check_change_timer(); start_motor_off_timer(); @@ -1990,10 +1990,6 @@ int __init atari_floppy_init (void) SelectedDrive = -1; BufferDrive = -1; - /* initialize check_change timer */ - timer_table[FLOPPY_TIMER].fn = check_change; - timer_active &= ~(1 << FLOPPY_TIMER); - DMABuffer = atari_stram_alloc( BUFFER_SIZE+512, NULL, "ataflop" ); if (!DMABuffer) { printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n"); @@ -2072,8 +2068,7 @@ void cleanup_module (void) unregister_blkdev(MAJOR_NR, "fd"); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - timer_active &= ~(1 << FLOPPY_TIMER); - timer_table[FLOPPY_TIMER].fn = 0; + del_timer_sync(&fd_timer); atari_stram_free( DMABuffer ); } #endif diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 9bbe50523..9ae6fa94c 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -331,6 +331,7 @@ void cleanup_module(void) iounmap((void*)hba[i]->vaddr); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); del_timer(&hba[i]->timer); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i)); remove_proc_entry(hba[i]->devname, proc_array); kfree(hba[i]->cmd_pool); kfree(hba[i]->cmd_pool_bits); diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 1eee6d545..7720c43c4 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -88,25 +88,25 @@ int elevator_default_merge(request_queue_t *q, struct request **req, head = head->next; while ((entry = entry->prev) != head && !starving) { - *req = blkdev_entry_to_request(entry); - latency += (*req)->nr_segments; - if (elevator_sequence_before((*req)->elevator_sequence, sequence)) + struct request *__rq = *req = blkdev_entry_to_request(entry); + latency += __rq->nr_segments; + if (elevator_sequence_before(__rq->elevator_sequence, sequence)) starving = 1; if (latency < 0) continue; - if ((*req)->sem) + if (__rq->sem) continue; - if ((*req)->cmd != rw) + if (__rq->cmd != rw) continue; - if ((*req)->nr_sectors + count > *max_sectors) + if (__rq->nr_sectors + count > *max_sectors) continue; - if ((*req)->rq_dev != bh->b_rdev) + if (__rq->rq_dev != bh->b_rdev) continue; - if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) { - if (latency - (*req)->nr_segments < 0) + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) { + if (latency - __rq->nr_segments < 0) break; action = ELEVATOR_BACK_MERGE; - } else if ((*req)->sector - count == bh->b_rsector) { + } else if (__rq->sector - count == bh->b_rsector) { if (starving) break; action = ELEVATOR_FRONT_MERGE; @@ -161,31 +161,45 @@ int elevator_linus_merge(request_queue_t *q, struct request **req, int *max_sectors, int *max_segments) { struct list_head *entry, *head = &q->queue_head; - unsigned int count = bh->b_size >> 9; + unsigned int count = bh->b_size >> 9, ret = ELEVATOR_NO_MERGE; entry = head; if (q->head_active && !q->plugged) head = head->next; while ((entry = entry->prev) != head) { - *req = blkdev_entry_to_request(entry); - if (!(*req)->elevator_sequence) + struct request *__rq = *req = blkdev_entry_to_request(entry); + if (!__rq->elevator_sequence) break; - if ((*req)->sem) + if (__rq->sem) continue; - if ((*req)->cmd != rw) + if (__rq->cmd != rw) continue; - if ((*req)->nr_sectors + count > *max_sectors) + if (__rq->nr_sectors + count > *max_sectors) continue; - if ((*req)->rq_dev != bh->b_rdev) + if (__rq->rq_dev != bh->b_rdev) continue; - if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) - return ELEVATOR_BACK_MERGE; - if ((*req)->sector - count == bh->b_rsector) - return ELEVATOR_FRONT_MERGE; - (*req)->elevator_sequence--; + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) { + ret = ELEVATOR_BACK_MERGE; + break; + } + if (__rq->sector - count == bh->b_rsector) { + ret = ELEVATOR_FRONT_MERGE; + break; + } } - return ELEVATOR_NO_MERGE; + + /* + * second pass scan of requests that got passed over, if any + */ + if (ret != ELEVATOR_NO_MERGE && *req) { + while ((entry = entry->next) != &q->queue_head) { + struct request *tmp = blkdev_entry_to_request(entry); + tmp->elevator_sequence--; + } + } + + return ret; } /* @@ -213,18 +227,18 @@ int elevator_noop_merge(request_queue_t *q, struct request **req, entry = head; while ((entry = entry->prev) != head) { - *req = blkdev_entry_to_request(entry); - if ((*req)->sem) + struct request *__rq = *req = blkdev_entry_to_request(entry); + if (__rq->sem) continue; - if ((*req)->cmd != rw) + if (__rq->cmd != rw) continue; - if ((*req)->nr_sectors + count > *max_sectors) + if (__rq->nr_sectors + count > *max_sectors) continue; - if ((*req)->rq_dev != bh->b_rdev) + if (__rq->rq_dev != bh->b_rdev) continue; - if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + if (__rq->sector + __rq->nr_sectors == bh->b_rsector) return ELEVATOR_BACK_MERGE; - if ((*req)->sector - count == bh->b_rsector) + if (__rq->sector - count == bh->b_rsector) return ELEVATOR_FRONT_MERGE; } return ELEVATOR_NO_MERGE; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 1fcd8b73f..18a2c6c91 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -1872,7 +1872,7 @@ static void show_floppy(void) if (timer_pending(&fd_timer)) printk("fd_timer.function=%p\n", fd_timer.function); if (timer_pending(&fd_timeout)){ - printk("timer_table=%p\n",fd_timeout.function); + printk("timer_function=%p\n",fd_timeout.function); printk("expires=%lu\n",fd_timeout.expires-jiffies); printk("now=%lu\n",jiffies); } @@ -4109,7 +4109,7 @@ int __init floppy_init(void) raw_cmd = NULL; - devfs_handle = devfs_mk_dir (NULL, "floppy", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "floppy", NULL); if (devfs_register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { printk("Unable to get major %d for floppy\n",MAJOR_NR); return -EBUSY; @@ -4143,6 +4143,7 @@ int __init floppy_init(void) if (fdc_state[0].address == -1) { devfs_unregister_blkdev(MAJOR_NR,"fd"); del_timer(&fd_timeout); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); return -ENODEV; } #if N_FDC > 1 diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 9646adbd0..7735594cb 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -148,34 +148,40 @@ request_queue_t *blk_get_queue(kdev_t dev) return ret; } -/* - * Hopefully the low level driver has finished any out standing requests - * first... - */ -void blk_cleanup_queue(request_queue_t * q) +static int __block_cleanup_queue(struct list_head *head) { struct list_head *entry; struct request *rq; - int i = QUEUE_NR_REQUESTS; + int i = 0; - if (list_empty(&q->request_freelist)) - return; - - if (q->queue_requests) - BUG(); + if (list_empty(head)) + return 0; - entry = &q->request_freelist; - entry = entry->next; + entry = head->next; do { rq = list_entry(entry, struct request, table); entry = entry->next; list_del(&rq->table); kmem_cache_free(request_cachep, rq); - i--; - } while (!list_empty(&q->request_freelist)); + i++; + } while (!list_empty(head)); + + return i; +} + +/* + * Hopefully the low level driver has finished any out standing requests + * first... + */ +void blk_cleanup_queue(request_queue_t * q) +{ + int count = QUEUE_NR_REQUESTS; - if (i) - printk("blk_cleanup_queue: leaked requests (%d)\n", i); + count -= __block_cleanup_queue(&q->request_freelist[READ]); + count -= __block_cleanup_queue(&q->request_freelist[WRITE]); + + if (count) + printk("blk_cleanup_queue: leaked requests (%d)\n", count); memset(q, 0, sizeof(*q)); } @@ -280,10 +286,9 @@ static void blk_init_free_list(request_queue_t *q) for (i = 0; i < QUEUE_NR_REQUESTS; i++) { rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); rq->rq_status = RQ_INACTIVE; - list_add(&rq->table, &q->request_freelist); + list_add(&rq->table, &q->request_freelist[i & 1]); } - q->queue_requests = 0; init_waitqueue_head(&q->wait_for_request); spin_lock_init(&q->request_lock); } @@ -291,7 +296,8 @@ static void blk_init_free_list(request_queue_t *q) void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) { INIT_LIST_HEAD(&q->queue_head); - INIT_LIST_HEAD(&q->request_freelist); + INIT_LIST_HEAD(&q->request_freelist[READ]); + INIT_LIST_HEAD(&q->request_freelist[WRITE]); elevator_init(&q->elevator, ELEVATOR_LINUS); blk_init_free_list(q); q->request_fn = rfn; @@ -342,19 +348,37 @@ void generic_unplug_device(void *data) */ static inline struct request *get_request(request_queue_t *q, int rw) { - register struct request *rq = NULL; + struct list_head *list = &q->request_freelist[rw]; + struct request *rq; - if (!list_empty(&q->request_freelist)) { - if ((q->queue_requests > QUEUE_WRITES_MAX) && (rw == WRITE)) - return NULL; + /* + * Reads get preferential treatment and are allowed to steal + * from the write free list if necessary. + */ + if (!list_empty(list)) { + rq = blkdev_free_rq(list); + goto got_rq; + } - rq = blkdev_free_rq(&q->request_freelist); - list_del(&rq->table); - rq->rq_status = RQ_ACTIVE; - rq->special = NULL; - rq->q = q; - q->queue_requests++; + /* + * if the WRITE list is non-empty, we know that rw is READ + * and that the READ list is empty. allow reads to 'steal' + * from the WRITE list. + */ + if (!list_empty(&q->request_freelist[WRITE])) { + list = &q->request_freelist[WRITE]; + rq = blkdev_free_rq(list); + goto got_rq; } + + return NULL; + +got_rq: + list_del(&rq->table); + rq->free_list = list; + rq->rq_status = RQ_ACTIVE; + rq->special = NULL; + rq->q = q; return rq; } @@ -486,9 +510,9 @@ void inline blkdev_release_request(struct request *req) /* * Request may not have originated from ll_rw_blk */ - if (req->q) { - list_add(&req->table, &req->q->request_freelist); - req->q->queue_requests--; + if (req->free_list) { + list_add(&req->table, req->free_list); + req->free_list = NULL; wake_up(&req->q->wait_for_request); } } @@ -557,7 +581,7 @@ static inline void __make_request(request_queue_t * q, int rw, int max_segments = MAX_SEGMENTS; struct request * req = NULL; int rw_ahead, max_sectors, el_ret; - struct list_head *head = &q->queue_head; + struct list_head *head; int latency; elevator_t *elevator = &q->elevator; @@ -602,12 +626,6 @@ static inline void __make_request(request_queue_t * q, int rw, goto end_io; /* Hmmph! Nothing to write */ refile_buffer(bh); do_write: - /* - * We don't allow the write-requests to fill up the - * queue completely: we want some room for reads, - * as they take precedence. The last third of the - * requests are only for reads. - */ kstat.pgpgout++; break; default: @@ -646,11 +664,6 @@ static inline void __make_request(request_queue_t * q, int rw, spin_lock_irq(&io_request_lock); elevator_default_debug(q, bh->b_rdev); - if (list_empty(head)) { - q->plug_device_fn(q, bh->b_rdev); /* is atomic */ - goto get_rq; - } - /* * skip first entry, for devices with active queue head */ @@ -658,6 +671,11 @@ static inline void __make_request(request_queue_t * q, int rw, if (q->head_active && !q->plugged) head = head->next; + if (list_empty(head)) { + q->plug_device_fn(q, bh->b_rdev); /* is atomic */ + goto get_rq; + } + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); switch (el_ret) { @@ -843,7 +861,6 @@ static void __ll_rw_block(int rw, int nr, struct buffer_head * bhs[], sorry: for (i = 0; i < nr; i++) buffer_IO_error(bhs[i]); - return; } void ll_rw_block(int rw, int nr, struct buffer_head * bh[]) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 89018e54a..ed52ee2b8 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -782,7 +782,7 @@ int __init loop_init(void) MAJOR_NR); return -EIO; } - devfs_handle = devfs_mk_dir (NULL, "loop", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "loop", NULL); devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, diff --git a/drivers/block/md.c b/drivers/block/md.c index 617990e77..3fa5e5318 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -804,6 +804,7 @@ static void free_mddev (mddev_t *mddev) del_mddev_mapping(mddev, MKDEV(MD_MAJOR, mdidx(mddev))); md_list_del(&mddev->all_mddevs); MD_INIT_LIST_HEAD(&mddev->all_mddevs); + blk_cleanup_queue(&mddev->queue); kfree(mddev); } @@ -3627,7 +3628,7 @@ int md__init md_init (void) printk (KERN_ALERT "Unable to get major %d for md\n", MD_MAJOR); return (-1); } - devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "md", NULL); devfs_register_series (devfs_handle, "%u",MAX_MD_DEVS,DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 222622c35..436969bb4 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -517,7 +517,7 @@ int nbd_init(void) register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &nbd_fops, nbd_bytesizes[i]>>9); } - devfs_handle = devfs_mk_dir (NULL, "nbd", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "nbd", NULL); devfs_register_series (devfs_handle, "%u", MAX_NBD, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, @@ -530,6 +530,7 @@ int nbd_init(void) void cleanup_module(void) { devfs_unregister (devfs_handle); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); if (unregister_blkdev(MAJOR_NR, "nbd") != 0) printk("nbd: cleanup_module failed\n"); diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 5c4dbbee8..c282d82a1 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -307,7 +307,7 @@ int pg_init (void) /* preliminary initialisation */ if (PG.present) pi_release(PI); return -1; } - devfs_handle = devfs_mk_dir (NULL, "pg", 2, NULL); + devfs_handle = devfs_mk_dir (NULL, "pg", NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pg_fops, NULL); diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 52c67d017..0d3e81838 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -312,7 +312,7 @@ int pt_init (void) /* preliminary initialisation */ return -1; } - devfs_handle = devfs_mk_dir (NULL, "pt", 2, NULL); + devfs_handle = devfs_mk_dir (NULL, "pt", NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pt_fops, NULL); diff --git a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c index 766ea0a18..fec570acb 100644 --- a/drivers/block/ps2esdi.c +++ b/drivers/block/ps2esdi.c @@ -232,6 +232,7 @@ cleanup_module(void) free_dma(dma_arb_level); free_irq(PS2ESDI_IRQ, NULL) devfs_unregister_blkdev(MAJOR_NR, "ed"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); } #endif /* MODULE */ diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 26c0509d2..727c1c543 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -408,7 +408,7 @@ int __init rd_init (void) rd_blocksizes[i] = rd_blocksize; rd_kbsize[i] = rd_size; } - devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "rd", NULL); devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, @@ -436,8 +436,8 @@ int __init rd_init (void) #ifdef MODULE module_init(rd_init); -#endif module_exit(rd_cleanup); +#endif /* loadable module support */ MODULE_PARM (rd_size, "1i"); diff --git a/drivers/block/xd.c b/drivers/block/xd.c index 5ac2a16a6..8bd2ec3bc 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -166,7 +166,7 @@ int __init xd_init (void) printk("xd: Unable to get major number %d\n",MAJOR_NR); return -1; } - devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, 0, NULL); + devfs_handle = devfs_mk_dir (NULL, xd_gendisk.major_name, NULL); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ xd_gendisk.next = gendisk_head; diff --git a/drivers/block/xd.h b/drivers/block/xd.h index d6fcc675a..1612f321d 100644 --- a/drivers/block/xd.h +++ b/drivers/block/xd.h @@ -103,9 +103,9 @@ typedef struct { const char *name; } XD_SIGNATURE; -int xd_setup (char *); +static int xd_setup (char *); #ifndef MODULE -int xd_manual_geo_init (char *command); +static int xd_manual_geo_init (char *command); #endif /* MODULE */ static u_char xd_detect (u_char *controller, unsigned int *address); static u_char xd_initdrives (void (*init_drive)(u_char drive)); diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 74ea30a41..88764463a 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -399,6 +399,8 @@ cleanup_module( void ) if ( unregister_blkdev( MAJOR_NR, DEVICE_NAME ) != 0 ) printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + if ( current_device != -1 ) { i = 0; diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index e32238d32..b3b73f2fc 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -1819,12 +1819,13 @@ int __init aztcd_init(void) void __exit aztcd_exit(void) { - devfs_unregister(devfs_find_handle(NULL, "aztcd", 0, 0, 0, DEVFS_SPECIAL_BLK, + devfs_unregister(devfs_find_handle(NULL, "aztcd", 0, 0, DEVFS_SPECIAL_BLK, 0)); if ((devfs_unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) { printk("What's that: can't unregister aztcd\n"); return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); if ((azt_port==0x1f0)||(azt_port==0x170)) { SWITCH_IDE_MASTER; release_region(azt_port,8); /*IDE-interface*/ diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index caa3900b3..4503bbfed 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -389,7 +389,7 @@ int register_cdrom(struct cdrom_device_info *cdi) cdi->options |= (int) CDO_CHECK_TYPE; if (!devfs_handle) - devfs_handle = devfs_mk_dir (NULL, "cdroms", 6, NULL); + devfs_handle = devfs_mk_dir (NULL, "cdroms", NULL); sprintf (vname, "cdrom%u", cdrom_counter++); if (cdi->de) { int pos; @@ -400,9 +400,9 @@ int register_cdrom(struct cdrom_device_info *cdi) sizeof rname - 3); if (pos >= 0) { strncpy (rname + pos, "../", 3); - devfs_mk_symlink (devfs_handle, vname, 0, + devfs_mk_symlink (devfs_handle, vname, DEVFS_FL_DEFAULT, - rname + pos, 0, &slave, NULL); + rname + pos, &slave, NULL); devfs_auto_unregister (cdi->de, slave); } } @@ -2617,7 +2617,7 @@ static int __init cdrom_init(void) #ifdef CONFIG_SYSCTL cdrom_sysctl_register(); #endif - devfs_handle = devfs_mk_dir(NULL, "cdroms", 6, NULL); + devfs_handle = devfs_mk_dir(NULL, "cdroms", NULL); return 0; } diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c index 0b8d942b4..56054ae9e 100644 --- a/drivers/cdrom/cdu31a.c +++ b/drivers/cdrom/cdu31a.c @@ -3544,6 +3544,7 @@ cdu31a_init(void) } errout0: printk("Unable to register CDU-31a with Uniform cdrom driver\n"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); if (devfs_unregister_blkdev(MAJOR_NR, "cdu31a")) { printk("Can't unregister block device for cdu31a\n"); @@ -3569,6 +3570,8 @@ cdu31a_exit(void) return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + if (cdu31a_irq > 0) free_irq(cdu31a_irq, NULL); diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c index 9e8889ac6..facf6223d 100644 --- a/drivers/cdrom/cm206.c +++ b/drivers/cdrom/cm206.c @@ -1285,6 +1285,7 @@ static void cleanup(int level) printk("Can't unregister major cm206\n"); return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); case 3: free_irq(cm206_irq, NULL); case 2: diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c index b0fa47b3e..dc8870491 100644 --- a/drivers/cdrom/gscd.c +++ b/drivers/cdrom/gscd.c @@ -95,7 +95,7 @@ static void gscd_bin2bcd (unsigned char *p); /* Schnittstellen zum Kern/FS */ static void do_gscd_request (request_queue_t *); -static void __do_gscd_request (void); +static void __do_gscd_request (unsigned long dummy); static int gscd_ioctl (struct inode *, struct file *, unsigned int, unsigned long); static int gscd_open (struct inode *, struct file *); static int gscd_release (struct inode *, struct file *); @@ -160,6 +160,7 @@ static int AudioStart_f; static int AudioEnd_m; static int AudioEnd_f; +static struct timer_list gscd_timer; static struct block_device_operations gscd_fops = { open: gscd_open, @@ -271,23 +272,24 @@ long offs; static void do_gscd_request (request_queue_t * q) { - __do_gscd_request(); + __do_gscd_request(0); } -static void __do_gscd_request (void) +static void __do_gscd_request (unsigned long dummy) { unsigned int block,dev; unsigned int nsect; repeat: - if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) return; + if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) + goto out; INIT_REQUEST; dev = MINOR(CURRENT->rq_dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; if (QUEUE_EMPTY || CURRENT -> sector == -1) - return; + goto out; if (CURRENT -> cmd != READ) { @@ -318,6 +320,8 @@ repeat: #endif gscd_read_cmd (); +out: + return; } @@ -992,13 +996,16 @@ long err; void __exit exit_gscd(void) { - devfs_unregister(devfs_find_handle(NULL, "gscd", 0, 0, 0, DEVFS_SPECIAL_BLK, + del_timer_async(&gscd_timer); + + devfs_unregister(devfs_find_handle(NULL, "gscd", 0, 0, DEVFS_SPECIAL_BLK, 0)); if ((devfs_unregister_blkdev(MAJOR_NR, "gscd" ) == -EINVAL)) { printk("What's that: can't unregister GoldStar-module\n" ); return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); release_region (gscd_port,4); printk(KERN_INFO "GoldStar-module released.\n" ); } diff --git a/drivers/cdrom/gscd.h b/drivers/cdrom/gscd.h index fccc742a5..9c294762b 100644 --- a/drivers/cdrom/gscd.h +++ b/drivers/cdrom/gscd.h @@ -74,11 +74,10 @@ #define READ_DATA(port, buf, nr) insb(port, buf, nr) #define SET_TIMER(func, jifs) \ - ((timer_table[GSCD_TIMER].expires = jiffies + jifs), \ - (timer_table[GSCD_TIMER].fn = func), \ - (timer_active |= 1<<GSCD_TIMER)) + ((mod_timer(&gscd_timer, jiffies + jifs)), \ + (gscd_timer.function = func)) -#define CLEAR_TIMER timer_active &= ~(1<<GSCD_TIMER) +#define CLEAR_TIMER del_timer_sync(&gscd_timer) #define MAX_TRACKS 104 diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 91bde1a1d..0f59831b7 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -182,7 +182,7 @@ static char tocUpToDate; static char mcdVersion; static void mcd_transfer(void); -static void mcd_poll(void); +static void mcd_poll(unsigned long dummy); static void mcd_invalidate_buffers(void); static void hsg2msf(long hsg, struct msf *msf); static void bin2bcd(unsigned char *p); @@ -203,6 +203,8 @@ int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, void * arg); int mcd_drive_status(struct cdrom_device_info * cdi, int slot_nr); +static struct timer_list mcd_timer; + static struct cdrom_device_ops mcd_dops = { mcd_open, /* open */ mcd_release, /* release */ @@ -705,7 +707,7 @@ do_mcd_request(request_queue_t * q) static void -mcd_poll(void) +mcd_poll(unsigned long dummy) { int st; @@ -787,7 +789,7 @@ mcd_poll(void) #ifdef TEST3 printk("MCD_S_IDLE\n"); #endif - return; + goto out; @@ -829,7 +831,7 @@ mcd_poll(void) mcd_state = MCD_S_IDLE; while (CURRENT_VALID) end_request(0); - return; + goto out; } outb(MCMD_SET_MODE, MCDPORT(0)); @@ -869,7 +871,7 @@ mcd_poll(void) mcd_state = MCD_S_IDLE; while (CURRENT_VALID) end_request(0); - return; + goto out; } if (CURRENT_VALID) { @@ -1071,13 +1073,13 @@ mcd_poll(void) } } else { mcd_state = MCD_S_IDLE; - return; + goto out; } break; default: printk("mcd: invalid state %d\n", mcd_state); - return; + goto out; } ret: @@ -1087,6 +1089,8 @@ mcd_poll(void) } SET_TIMER(mcd_poll, 1); +out: + return; } @@ -1169,6 +1173,7 @@ static void cleanup(int level) printk(KERN_WARNING "Can't unregister major mcd\n"); return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); default: } } @@ -1369,7 +1374,7 @@ sendMcdCmd(int cmd, struct mcd_Play_msf *params) */ static void -mcdStatTimer(void) +mcdStatTimer(unsigned long dummy) { if (!(inb(MCDPORT(1)) & MFL_STATUS)) { @@ -1659,6 +1664,7 @@ Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); void __exit mcd_exit(void) { cleanup(3); + del_timer_sync(&mcd_timer); } #ifdef MODULE diff --git a/drivers/cdrom/mcd.h b/drivers/cdrom/mcd.h index fffaa92a6..ce903d2f2 100644 --- a/drivers/cdrom/mcd.h +++ b/drivers/cdrom/mcd.h @@ -73,12 +73,13 @@ #define READ_DATA(port, buf, nr) \ insb(port, buf, nr) -#define SET_TIMER(func, jifs) \ - ((timer_table[MCD_TIMER].expires = jiffies + jifs), \ - (timer_table[MCD_TIMER].fn = func), \ - (timer_active |= 1<<MCD_TIMER)) +#define SET_TIMER(func, jifs) \ + do { \ + mcd_timer.function = func; \ + mod_timer(&mcd_timer, jiffies + jifs); \ + } while (0) -#define CLEAR_TIMER timer_active &= ~(1<<MCD_TIMER) +#define CLEAR_TIMER del_timer_async(&mcd_timer); #define MAX_TRACKS 104 diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c index 805044841..574ee429e 100644 --- a/drivers/cdrom/mcdx.c +++ b/drivers/cdrom/mcdx.c @@ -1016,6 +1016,7 @@ void __exit mcdx_exit(void) if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) { xwarn("cleanup() unregister_blkdev() failed\n"); } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); #if !MCDX_QUIET else xinfo("cleanup() succeeded\n"); #endif @@ -1143,6 +1144,7 @@ int __init mcdx_init_drive(int drive) MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq); stuffp->irq = 0; + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); kfree(stuffp); return 0; } @@ -1184,6 +1186,7 @@ int __init mcdx_init_drive(int drive) kfree(stuffp); if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) xwarn("cleanup() unregister_blkdev() failed\n"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); return 2; } printk(msg); diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index 480a01833..f851cd074 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -2080,12 +2080,13 @@ int __init optcd_init(void) void __exit optcd_exit(void) { - devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0, 0, + devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0, DEVFS_SPECIAL_BLK, 0)); if (devfs_unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) { printk(KERN_ERR "optcd: what's that: can't unregister\n"); return; } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); release_region(optcd_port, 4); printk(KERN_INFO "optcd: module released.\n"); } diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 08400a52c..2f65bb525 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -5752,7 +5752,7 @@ int __init SBPCD_INIT(void) request_region(CDo_command,4,major_name); - devfs_handle = devfs_mk_dir (NULL, "sbp", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "sbp", NULL); for (j=0;j<NR_SBPCD;j++) { struct cdrom_device_info * sbpcd_infop; @@ -5779,6 +5779,7 @@ int __init SBPCD_INIT(void) printk("Can't unregister %s\n", major_name); } release_region(CDo_command,4); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); return -EIO; } #ifdef MODULE @@ -5794,6 +5795,7 @@ int __init SBPCD_INIT(void) if (sbpcd_infop == NULL) { release_region(CDo_command,4); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); return -ENOMEM; } D_S[j].sbpcd_infop = sbpcd_infop; @@ -5845,7 +5847,7 @@ void sbpcd_exit(void) return; } release_region(CDo_command,4); - + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); devfs_unregister (devfs_handle); for (j=0;j<NR_SBPCD;j++) { diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c index a867c3819..b793bac4b 100644 --- a/drivers/cdrom/sjcd.c +++ b/drivers/cdrom/sjcd.c @@ -1577,8 +1577,10 @@ sjcd_cleanup(void) { if( (devfs_unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL) ) printk( "SJCD: cannot unregister device.\n" ); - else + else { release_region( sjcd_base, 4 ); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + } return(0); } @@ -1586,8 +1588,7 @@ sjcd_cleanup(void) void __exit sjcd_exit(void) { - devfs_unregister(devfs_find_handle(NULL, "sjcd", 0, 0, 0, DEVFS_SPECIAL_BLK, - 0)); + devfs_unregister(devfs_find_handle(NULL, "sjcd", 0, 0, DEVFS_SPECIAL_BLK,0)); if ( sjcd_cleanup() ) printk( "SJCD: module: cannot be removed.\n" ); else diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index d85fbc01f..945facb97 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -1603,17 +1603,21 @@ sony535_init(void) sony_toc = (struct s535_sony_toc *) kmalloc(sizeof *sony_toc, GFP_KERNEL); - if (sony_toc == NULL) + if (sony_toc == NULL) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); return -ENOMEM; + } last_sony_subcode = (struct s535_sony_subcode *) kmalloc(sizeof *last_sony_subcode, GFP_KERNEL); if (last_sony_subcode == NULL) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); kfree(sony_toc); return -ENOMEM; } sony_buffer = (Byte **) kmalloc(4 * sony_buffer_sectors, GFP_KERNEL); if (sony_buffer == NULL) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); kfree(sony_toc); kfree(last_sony_subcode); return -ENOMEM; @@ -1624,6 +1628,7 @@ sony535_init(void) if (sony_buffer[i] == NULL) { while (--i>=0) kfree(sony_buffer[i]); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); kfree(sony_buffer); kfree(sony_toc); kfree(last_sony_subcode); @@ -1690,7 +1695,7 @@ sony535_exit(void) kfree_s(sony_buffer, 4 * sony_buffer_sectors); kfree_s(last_sony_subcode, sizeof *last_sony_subcode); kfree_s(sony_toc, sizeof *sony_toc); - devfs_unregister(devfs_find_handle(NULL, CDU535_HANDLE, 0, 0, 0, + devfs_unregister(devfs_find_handle(NULL, CDU535_HANDLE, 0, 0, DEVFS_SPECIAL_BLK, 0)); if (devfs_unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL) printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n"); diff --git a/drivers/char/console.c b/drivers/char/console.c index 4b33e7433..6e862ec53 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -66,6 +66,9 @@ * * Resurrected character buffers in videoram plus lots of other trickery * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998 + * + * Removed old-style timers, introduced console_timer, made timer + * deletion SMP-safe. 17Jun00, Andrew Morton <andrewm@uow.edu.au> */ #include <linux/module.h> @@ -139,7 +142,7 @@ static struct consw *con_driver_map[MAX_NR_CONSOLES]; static int con_open(struct tty_struct *, struct file *); static void vc_init(unsigned int console, unsigned int rows, unsigned int cols, int do_clear); -static void blank_screen(void); +static void blank_screen(unsigned long dummy); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); static void reset_terminal(int currcons, int do_clear); @@ -147,6 +150,7 @@ static void con_flush_chars(struct tty_struct *tty); static void set_vesa_blanking(unsigned long arg); static void set_cursor(int currcons); static void hide_cursor(int currcons); +static void unblank_screen_t(unsigned long dummy); static int printable = 0; /* Is console ready for printing? */ @@ -196,6 +200,8 @@ static int scrollback_delta = 0; */ int (*console_blank_hook)(int) = NULL; +static struct timer_list console_timer; + /* * Low-Level Functions */ @@ -2417,11 +2423,10 @@ void __init con_init(void) if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); - timer_table[BLANK_TIMER].fn = blank_screen; - timer_table[BLANK_TIMER].expires = 0; + init_timer(&console_timer); + console_timer.function = blank_screen; if (blankinterval) { - timer_table[BLANK_TIMER].expires = jiffies + blankinterval; - timer_active |= 1<<BLANK_TIMER; + mod_timer(&console_timer, jiffies + blankinterval); } /* @@ -2589,15 +2594,14 @@ static void vesa_powerdown(void) } } -static void vesa_powerdown_screen(void) +static void vesa_powerdown_screen(unsigned long dummy) { - timer_active &= ~(1<<BLANK_TIMER); - timer_table[BLANK_TIMER].fn = unblank_screen; + console_timer.function = unblank_screen_t; /* I don't have a clue why this is necessary */ vesa_powerdown(); } -void do_blank_screen(int entering_gfx) +static void timer_do_blank_screen(int entering_gfx, int from_timer_handler) { int currcons = fg_console; int i; @@ -2622,13 +2626,15 @@ void do_blank_screen(int entering_gfx) } hide_cursor(currcons); + if (!from_timer_handler) + del_timer_sync(&console_timer); if (vesa_off_interval) { - timer_table[BLANK_TIMER].fn = vesa_powerdown_screen; - timer_table[BLANK_TIMER].expires = jiffies + vesa_off_interval; - timer_active |= (1<<BLANK_TIMER); + console_timer.function = vesa_powerdown_screen; + mod_timer(&console_timer, jiffies + vesa_off_interval); } else { - timer_active &= ~(1<<BLANK_TIMER); - timer_table[BLANK_TIMER].fn = unblank_screen; + if (!from_timer_handler) + del_timer_sync(&console_timer); + console_timer.function = unblank_screen_t; } save_screen(currcons); @@ -2644,6 +2650,16 @@ void do_blank_screen(int entering_gfx) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } +void do_blank_screen(int entering_gfx) +{ + timer_do_blank_screen(entering_gfx, 0); +} + +static void unblank_screen_t(unsigned long dummy) +{ + unblank_screen(); +} + void unblank_screen(void) { int currcons; @@ -2655,10 +2671,9 @@ void unblank_screen(void) printk("unblank_screen: tty %d not allocated ??\n", fg_console+1); return; } - timer_table[BLANK_TIMER].fn = blank_screen; + console_timer.function = blank_screen; if (blankinterval) { - timer_table[BLANK_TIMER].expires = jiffies + blankinterval; - timer_active |= 1<<BLANK_TIMER; + mod_timer(&console_timer, jiffies + blankinterval); } currcons = fg_console; @@ -2671,23 +2686,21 @@ void unblank_screen(void) set_cursor(fg_console); } -static void blank_screen(void) +static void blank_screen(unsigned long dummy) { - do_blank_screen(0); + timer_do_blank_screen(0, 1); } void poke_blanked_console(void) { - timer_active &= ~(1<<BLANK_TIMER); + del_timer(&console_timer); /* Can't use _sync here: called from tasklet */ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; if (console_blanked) { - timer_table[BLANK_TIMER].fn = unblank_screen; - timer_table[BLANK_TIMER].expires = jiffies; /* Now */ - timer_active |= 1<<BLANK_TIMER; + console_timer.function = unblank_screen_t; + mod_timer(&console_timer, jiffies); /* Now */ } else if (blankinterval) { - timer_table[BLANK_TIMER].expires = jiffies + blankinterval; - timer_active |= 1<<BLANK_TIMER; + mod_timer(&console_timer, jiffies + blankinterval); } } diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 13656af4b..2ba24a26b 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -127,6 +127,8 @@ static struct channel digi_channels[MAX_ALLOC]; -------------------------------------------------------------------------- */ static struct channel *card_ptr[MAXCARDS]; +static struct timer_list epca_timer; + /* ---------------------- Begin function prototypes --------------------- */ /* ---------------------------------------------------------------------- @@ -1564,12 +1566,11 @@ void cleanup_module() struct channel *ch; unsigned long flags; + del_timer_sync(&epca_timer); save_flags(flags); cli(); - timer_table[DIGI_TIMER].fn = 0; - if ((tty_unregister_driver(&pc_driver)) || (tty_unregister_driver(&pc_callout))) { @@ -1918,12 +1919,12 @@ int __init pc_init(void) Start up the poller to check for events on all enabled boards ---------------------------------------------------------------------- */ - timer_table[DIGI_TIMER].fn = (void *)epcapoll; - timer_table[DIGI_TIMER].expires = 0; + init_timer(&epca_timer); + epca_timer.function = epcapoll; + mod_timer(&epca_timer, jiffies + HZ/25); restore_flags(flags); - timer_active |= 1 << DIGI_TIMER; return 0; } /* End pc_init */ @@ -2267,12 +2268,9 @@ static void epcapoll(unsigned long ignored) } /* End for each card */ - timer_table[DIGI_TIMER].fn = (void *)epcapoll; - timer_table[DIGI_TIMER].expires = jiffies + (HZ / 25); - timer_active |= 1 << DIGI_TIMER; + mod_timer(&epca_timer, jiffies + (HZ / 25)); restore_flags(flags); - } /* End epcapoll */ /* --------------------- Begin doevent ------------------------ */ diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index e6cb87d1e..fd81f24ca 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -521,17 +521,17 @@ void cleanup_module(void) } for (i = 0; i < 4; i++) { sprintf(devname, "qft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i, DEVFS_SPECIAL_CHR, 0)); sprintf(devname, "nqft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 4, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i + 4, DEVFS_SPECIAL_CHR, 0)); sprintf(devname, "zqft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 16, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i + 16, DEVFS_SPECIAL_CHR, 0)); sprintf(devname, "nzqft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 20, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i + 20, DEVFS_SPECIAL_CHR, 0)); sprintf(devname, "rawqft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 32, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i + 32, DEVFS_SPECIAL_CHR, 0)); sprintf(devname, "nrawqft%i", i); - devfs_unregister(devfs_find_handle(NULL, devname, 0, QIC117_TAPE_MAJOR, i + 36, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, QIC117_TAPE_MAJOR, i + 36, DEVFS_SPECIAL_CHR, 0)); } zft_uninit_mem(); /* release remaining memory, if any */ printk(KERN_INFO "zftape successfully unloaded.\n"); diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c index c45509bcd..95af0a0f1 100644 --- a/drivers/char/ip2/i2ellis.c +++ b/drivers/char/ip2/i2ellis.c @@ -57,7 +57,7 @@ static int iiDelayed = 0; // Set when the iiResetDelay function is // called. Cleared when ANY board is reset. static struct timer_list * pDelayTimer; // Used by iiDelayTimer static wait_queue_head_t pDelayWait; // Used by iiDelayTimer -static spinlock_t Dl_spinlock; +static rwlock_t Dl_spinlock; //******** //* Code * diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h index 5bbc4cca2..e249665c1 100644 --- a/drivers/char/ip2/i2ellis.h +++ b/drivers/char/ip2/i2ellis.h @@ -352,7 +352,7 @@ typedef struct _i2eBordStr // Ring-buffers of channel structures whose channels have particular needs. - spinlock_t Fbuf_spinlock; + rwlock_t Fbuf_spinlock; volatile unsigned short i2Fbuf_strip; // Strip index volatile @@ -360,7 +360,7 @@ typedef struct _i2eBordStr void *i2Fbuf[CH_QUEUE_SIZE]; // An array of channel pointers // of channels who need to send // flow control packets. - spinlock_t Dbuf_spinlock; + rwlock_t Dbuf_spinlock; volatile unsigned short i2Dbuf_strip; // Strip index volatile @@ -368,7 +368,7 @@ typedef struct _i2eBordStr void *i2Dbuf[CH_QUEUE_SIZE]; // An array of channel pointers // of channels who need to send // data or in-line command packets. - spinlock_t Bbuf_spinlock; + rwlock_t Bbuf_spinlock; volatile unsigned short i2Bbuf_strip; // Strip index volatile @@ -397,8 +397,8 @@ typedef struct _i2eBordStr unsigned long debugInlineCount; unsigned long debugBypassCount; - spinlock_t read_fifo_spinlock; - spinlock_t write_fifo_spinlock; + rwlock_t read_fifo_spinlock; + rwlock_t write_fifo_spinlock; #ifdef CONFIG_DEVFS_FS /* Device handles into devfs */ diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c index 5fc1fe7a6..310044ab8 100644 --- a/drivers/char/ip2/i2lib.c +++ b/drivers/char/ip2/i2lib.c @@ -529,7 +529,7 @@ i2QueueCommands(int type, i2ChanStrPtr pCh, int timeout, int nCommands, unsigned short channel; int cnt; unsigned long flags = 0; - spinlock_t *lock_var_p = NULL; + rwlock_t *lock_var_p = NULL; // Make sure the channel exists, otherwise do nothing if ( !i2Validate ( pCh ) ) { diff --git a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h index ba5646f06..8e3f707d3 100644 --- a/drivers/char/ip2/i2lib.h +++ b/drivers/char/ip2/i2lib.h @@ -228,10 +228,10 @@ typedef struct _i2ChanStr struct tq_struct tqueue_status; struct tq_struct tqueue_hangup; - spinlock_t Ibuf_spinlock; - spinlock_t Obuf_spinlock; - spinlock_t Cbuf_spinlock; - spinlock_t Pbuf_spinlock; + rwlock_t Ibuf_spinlock; + rwlock_t Obuf_spinlock; + rwlock_t Cbuf_spinlock; + rwlock_t Pbuf_spinlock; } i2ChanStr, *i2ChanStrPtr; diff --git a/drivers/char/ip2/i2os.h b/drivers/char/ip2/i2os.h index f85822c1a..8466d7747 100644 --- a/drivers/char/ip2/i2os.h +++ b/drivers/char/ip2/i2os.h @@ -27,6 +27,7 @@ #include "ip2types.h" #include <asm/io.h> /* For inb, etc */ +#include <linux/version.h> //------------------------------------ // Defines for I/O instructions: @@ -61,7 +62,7 @@ typedef int spinlock_t; //#define SAVE_AND_DISABLE_INTS(a,b) spin_lock_irqsave(a,b) //#define RESTORE_INTS(a,b) spin_unlock_irqrestore(a,b) -#define LOCK_INIT(a) spin_lock_init(a) +#define LOCK_INIT(a) rwlock_init(a) #define SAVE_AND_DISABLE_INTS(a,b) { \ /* printk("get_lock: 0x%x,%4d,%s\n",(int)a,__LINE__,__FILE__);*/ \ diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c index a645832c4..449bd2f76 100644 --- a/drivers/char/ip2main.c +++ b/drivers/char/ip2main.c @@ -329,7 +329,7 @@ static long bh_counter = 0; * selected, the board is serviced periodically to see if anything needs doing. */ #define POLL_TIMEOUT (jiffies + 1) -static struct timer_list PollTimer = { NULL, NULL, 0, 0, ip2_poll }; +static struct timer_list PollTimer = { {NULL, NULL}, 0, 0, ip2_poll }; // next, prev, expires,data, func() static char TimerOn = 0; @@ -862,7 +862,7 @@ old_ip2_init(void) */ #ifdef CONFIG_DEVFS_FS if (!devfs_handle) - devfs_handle = devfs_mk_dir (NULL, "ip2", 3, NULL); + devfs_handle = devfs_mk_dir (NULL, "ip2", NULL); #endif for( i = 0; i < IP2_MAX_BOARDS; ++i ) { @@ -2568,10 +2568,10 @@ set_serial_info( i2ChanStrPtr pCh, struct serial_struct *new_info ) * base. Also line nunber as such is meaningless but we use it for our * array index so it is fixed also. */ - if ( ns.irq != ip2config.irq - || (int) ns.port != ((int) pCh->pMyBord->i2eBase) - || ns.baud_base != pCh->BaudBase - || ns.line != pCh->port_index ) { + if ( (ns.irq != ip2config.irq[pCh->port_index]) + || ((int) ns.port != ((int) (pCh->pMyBord->i2eBase))) + || (ns.baud_base != pCh->BaudBase) + || (ns.line != pCh->port_index) ) { return -EINVAL; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 81421c032..bee522007 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -5307,7 +5307,7 @@ int __init stli_init(void) if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stli_fsiomem)) printk("STALLION: failed to register serial memory device\n"); - devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); + devfs_handle = devfs_mk_dir (NULL, "staliomem", NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, diff --git a/drivers/char/lp.c b/drivers/char/lp.c index bbbcad9a7..5e212af34 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -771,7 +771,7 @@ int __init lp_init (void) return -EIO; } - devfs_handle = devfs_mk_dir (NULL, "printers", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "printers", NULL); if (parport_register_driver (&lp_driver)) { printk ("lp: unable to register with parport\n"); diff --git a/drivers/char/misc.c b/drivers/char/misc.c index c26a58eea..274f3a726 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -135,8 +135,11 @@ static int misc_open(struct inode * inode, struct file * file) old_fops = file->f_op; file->f_op = fops_get(c->fops); - if (file->f_op && file->f_op->open) - err=file->f_op->open(inode,file); + if (file->f_op) { + err = 0; + if (file->f_op->open) + err=file->f_op->open(inode,file); + } if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); @@ -191,7 +194,7 @@ int misc_register(struct miscdevice * misc) if (misc->minor < DYNAMIC_MINORS) misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); if (!devfs_handle) - devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL); + devfs_handle = devfs_mk_dir (NULL, "misc", NULL); misc->devfs_handle = devfs_register (devfs_handle, misc->name, DEVFS_FL_NONE, MISC_MAJOR, misc->minor, diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index d6fa8314b..dce40c671 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -85,7 +85,9 @@ static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), the autoscan seems work well only with FM... */ static int simple = -1; /* use short programming (>= msp3410 only) */ static int dolby = 0; +#ifdef REGISTER_MIXER static int mixer = -1; +#endif struct msp3400c { int simple; diff --git a/drivers/char/nwbutton.h b/drivers/char/nwbutton.h index ba5067443..5f5c8ddd0 100644 --- a/drivers/char/nwbutton.h +++ b/drivers/char/nwbutton.h @@ -28,8 +28,6 @@ static void button_sequence_finished (unsigned long parameters); static void button_handler (int irq, void *dev_id, struct pt_regs *regs); static int button_read (struct file *filp, char *buffer, size_t count, loff_t *ppos); -static int button_open (struct inode *inode, struct file *filp); -static int button_release (struct inode *inode, struct file *filp); int button_init (void); int button_add_callback (void (*callback) (void), int count); int button_del_callback (void (*callback) (void)); diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index 2e451ce17..757af9c14 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -151,9 +151,11 @@ struct tty_driver pcxe_driver; struct tty_driver pcxe_callout; static int pcxe_refcount; +static struct timer_list pcxx_timer; + DECLARE_TASK_QUEUE(tq_pcxx); -static void pcxxpoll(void); +static void pcxxpoll(unsigned long dummy); static void pcxxdelay(int); static void fepcmd(struct channel *, int, int, int, int, int); static void pcxe_put_char(struct tty_struct *, unsigned char); @@ -216,9 +218,7 @@ void cleanup_module() save_flags(flags); cli(); - timer_active &= ~(1 << DIGI_TIMER); - timer_table[DIGI_TIMER].fn = NULL; - timer_table[DIGI_TIMER].expires = 0; + del_timer_sync(&pcxx_timer); remove_bh(DIGI_BH); if ((e1 = tty_unregister_driver(&pcxe_driver))) @@ -1199,8 +1199,8 @@ int __init pcxe_init(void) init_bh(DIGI_BH,do_pcxe_bh); - timer_table[DIGI_TIMER].fn = pcxxpoll; - timer_table[DIGI_TIMER].expires = 0; + init_timer(&pcxx_timer); + pcxx_timer.function = pcxxpoll; memset(&pcxe_driver, 0, sizeof(struct tty_driver)); pcxe_driver.magic = TTY_DRIVER_MAGIC; @@ -1620,7 +1620,7 @@ load_fep: /* * Start up the poller to check for events on all enabled boards */ - timer_active |= 1 << DIGI_TIMER; + mod_timer(&pcxx_timer, HZ/25); if (verbose) printk(KERN_NOTICE "PC/Xx: Driver with %d card(s) ready.\n", enabled_cards); @@ -1629,7 +1629,7 @@ load_fep: } -static void pcxxpoll(void) +static void pcxxpoll(unsigned long dummy) { unsigned long flags; int crd; @@ -1660,9 +1660,7 @@ static void pcxxpoll(void) memoff(ch); } - timer_table[DIGI_TIMER].fn = pcxxpoll; - timer_table[DIGI_TIMER].expires = jiffies + HZ/25; - timer_active |= 1 << DIGI_TIMER; + mod_timer(&pcxx_timer, jiffies + HZ/25); restore_flags(flags); } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index ae4b9e112..4e2be3c8b 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -105,9 +105,9 @@ static void pp_attach (struct parport *port) return; } - add->next = pp_port_list; add->port = port; down (&pp_port_list_lock); + add->next = pp_port_list; pp_port_list = add; up (&pp_port_list_lock); } @@ -663,7 +663,7 @@ static int __init ppdev_init (void) PP_MAJOR); return -EIO; } - devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "parports", NULL); devfs_register_series (devfs_handle, "%u", PARPORT_MAX, DEVFS_FL_DEFAULT, PP_MAJOR, 0, S_IFCHR | S_IRUGO | S_IWUGO, diff --git a/drivers/char/pty.c b/drivers/char/pty.c index da6da97d7..4d11a2448 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -429,7 +429,7 @@ int __init pty_init(void) /* Unix98 devices */ #ifdef CONFIG_UNIX98_PTYS - devfs_mk_dir (NULL, "pts", 3, NULL); + devfs_mk_dir (NULL, "pts", NULL); printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS); for ( i = 0 ; i < UNIX98_NR_MAJORS ; i++ ) { int j; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 943ea6a61..af94b4b3f 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -138,13 +138,6 @@ #define _INLINE_ inline -/* - * Until we get a formal timer assignment - */ -#ifndef COMTROL_TIMER -#define COMTROL_TIMER 26 -#endif - #ifndef NEW_MODULES /* * NB. we must include the kernel idenfication string in to install the module. @@ -165,6 +158,8 @@ static int rocket_refcount = 0; static int rp_num_ports_open = 0; +static struct timer_list rocket_timer; + unsigned long board1 = 0; unsigned long board2 = 0; unsigned long board3 = 0; @@ -504,7 +499,7 @@ static _INLINE_ void rp_handle_port(struct r_port *info) /* * The top level polling routine. */ -static void rp_do_poll(void) +static void rp_do_poll(unsigned long dummy) { CONTROLLER_t *ctlp; int ctrl, aiop, ch, line; @@ -556,7 +551,7 @@ static void rp_do_poll(void) * Reset the timer so we get called at the next clock tick. */ if (rp_num_ports_open) { - timer_active |= 1 << COMTROL_TIMER; + mod_timer(&rocket_timer, jiffies + 1); } #ifdef TIME_STAT __asm__(".byte 0x0f,0x31" @@ -1044,7 +1039,7 @@ static int rp_open(struct tty_struct *tty, struct file * filp) sSetRTS(cp); } - timer_active |= 1 << COMTROL_TIMER; + mod_timer(&rocket_timer, jiffies + 1); retval = block_til_ready(tty, filp, info); if (retval) { @@ -2145,13 +2140,12 @@ int __init rp_init(void) * Set up the timer channel. If it is already in use by * some other driver, give up. */ - if (timer_table[COMTROL_TIMER].fn) { - printk("rocket.o: Timer channel %d already in use!\n", - COMTROL_TIMER); + if (rocket_timer.function) { + printk("rocket.o: Timer already in use!\n"); return -EBUSY; } - timer_table[COMTROL_TIMER].fn = rp_do_poll; - timer_table[COMTROL_TIMER].expires = 0; + init_timer(&rocket_timer); + rocket_timer.function = rp_do_poll; /* * Initialize the array of pointers to our own internal state @@ -2208,7 +2202,7 @@ int __init rp_init(void) if (max_board == 0) { printk("No rocketport ports found; unloading driver.\n"); - timer_table[COMTROL_TIMER].fn = 0; + rocket_timer.function = 0; return -ENODEV; } @@ -2300,7 +2294,9 @@ cleanup_module( void) { int retval; int i; int released_controller = 0; - + + del_timer_sync(&rocket_timer); + retval = tty_unregister_driver(&callout_driver); if (retval) { printk("Error %d while trying to unregister " @@ -2328,7 +2324,7 @@ cleanup_module( void) { } if (tmp_buf) free_page((unsigned long) tmp_buf); - timer_table[COMTROL_TIMER].fn = 0; + rocket_timer.function = 0; } #endif diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 82d1bf131..fbbc8271c 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -46,6 +46,9 @@ * 5/00: Support for the RSA-DV II/S card added. * Kiyokazu SUTO <suto@ks-and-ks.ne.jp> * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton <andrewm@uow.edu.au> + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -232,6 +235,8 @@ static DECLARE_TASK_QUEUE(tq_serial); static struct tty_driver serial_driver, callout_driver; static int serial_refcount; +static struct timer_list serial_timer; + /* serial subtype definitions */ #ifndef SERIAL_TYPE_NORMAL #define SERIAL_TYPE_NORMAL 1 @@ -304,11 +309,10 @@ static struct serial_state rs_table[RS_TABLE_SIZE] = { #if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) #define NR_PCI_BOARDS 8 -#ifdef MODULE /* We don't unregister PCI boards right now */ static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; static int serial_pci_board_idx = 0; -#endif + #ifndef IS_PCI_REGION_IOPORT #define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ IORESOURCE_IO) @@ -1023,7 +1027,7 @@ static void do_softint(void *private_) * passable results for a 16550A. (Although at the expense of much * CPU overhead). */ -static void rs_timer(void) +static void rs_timer(unsigned long dummy) { static unsigned long last_strobe = 0; struct async_struct *info; @@ -1057,8 +1061,7 @@ static void rs_timer(void) } } last_strobe = jiffies; - timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME; - timer_active |= 1 << RS_TIMER; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); if (IRQ_ports[0]) { save_flags(flags); cli(); @@ -1069,7 +1072,7 @@ static void rs_timer(void) #endif restore_flags(flags); - timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2; + mod_timer(&serial_timer, jiffies + IRQ_timeout[0] - 2); } } @@ -1380,8 +1383,7 @@ static int startup(struct async_struct * info) /* * Set up serial timers... */ - timer_table[RS_TIMER].expires = jiffies + 2*HZ/100; - timer_active |= 1 << RS_TIMER; + mod_timer(&serial_timer, jiffies + 2*HZ/100); /* * Set up the tty->alt_speed kludge @@ -4980,12 +4982,12 @@ static void __init probe_serial_pnp(void) /* * The serial driver boot-time initialization code! */ -int __init rs_init(void) +static int __init rs_init(void) { int i; struct serial_state * state; - if (timer_table[RS_TIMER].fn) { + if (serial_timer.function) { printk("RS_TIMER already set, another serial driver " "already loaded?\n"); #ifdef MODULE @@ -4996,8 +4998,9 @@ int __init rs_init(void) } init_bh(SERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); for (i = 0; i < NR_IRQS; i++) { IRQ_ports[i] = 0; @@ -5268,8 +5271,7 @@ void unregister_serial(int line) restore_flags(flags); } -#ifdef MODULE -void rs_fini(void) +static void __exit rs_fini(void) { unsigned long flags; int e1, e2; @@ -5277,10 +5279,8 @@ void rs_fini(void) struct async_struct *info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ + del_timer_sync(&serial_timer); save_flags(flags); cli(); - timer_active &= ~(1 << RS_TIMER); - timer_table[RS_TIMER].fn = NULL; - timer_table[RS_TIMER].expires = 0; remove_bh(SERIAL_BH); if ((e1 = tty_unregister_driver(&serial_driver))) printk("serial: failed to unregister serial driver (%d)\n", @@ -5326,7 +5326,6 @@ void rs_fini(void) free_page(pg); } } -#endif /* MODULE */ module_init(rs_init); module_exit(rs_fini); diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 0776b5d6b..eab300d5f 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -2338,7 +2338,6 @@ int specialix_init(void) #ifdef CONFIG_PCI if (pci_present()) { struct pci_dev *pdev = NULL; - unsigned int tint; i=0; while (i <= SX_NBOARD) { diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 235b0f131..9600c1bad 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3191,7 +3191,7 @@ int __init stl_init(void) */ if (devfs_register_chrdev(STL_SIOMEMMAJOR, "staliomem", &stl_fsiomem)) printk("STALLION: failed to register serial board device\n"); - devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); + devfs_handle = devfs_mk_dir (NULL, "staliomem", NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, diff --git a/drivers/char/stradis.c b/drivers/char/stradis.c index 949aa9532..03ca27a25 100644 --- a/drivers/char/stradis.c +++ b/drivers/char/stradis.c @@ -343,6 +343,7 @@ static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) return result; } +#if 0 /* unused */ /* MUST be a multiple of 8 bytes and 8-byte aligned and < 32768 bytes */ /* data copied into saa->dmadebi buffer, caller must re-enable interrupts */ static void ibm_block_dram_read(struct saa7146 *saa, int address, int bytes) @@ -375,6 +376,7 @@ static void ibm_block_dram_read(struct saa7146 *saa, int address, int bytes) buf[j] = debiread(saa, debNormal, IBM_MP2_DRAM_DATA, 4); } } +#endif /* unused */ static void do_irq_send_data(struct saa7146 *saa) { diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 8eb55b500..665514727 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -373,7 +373,7 @@ struct mgsl_struct { /* * The size of the serial xmit buffer is 1 page, or 4096 bytes */ -#define SERIAL_XMIT_SIZE 4096 +/* #define SERIAL_XMIT_SIZE 4096 */ /* defined in include/linux/serial.h */ /* @@ -7428,7 +7428,6 @@ BOOLEAN mgsl_memory_test( struct mgsl_struct *info ) } /* End Of mgsl_memory_test() */ -#pragma optimize( "", off ) /* mgsl_load_pci_memory() * * Load a large block of data into the PCI shared memory. @@ -7483,7 +7482,7 @@ void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, for ( Index = 0 ; Index < Intervalcount ; Index++ ) { memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL); - Dummy = *((unsigned long *)TargetPtr); + Dummy = *((volatile unsigned long *)TargetPtr); TargetPtr += PCI_LOAD_INTERVAL; SourcePtr += PCI_LOAD_INTERVAL; } @@ -7491,7 +7490,6 @@ void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL ); } /* End Of mgsl_load_pci_memory() */ -#pragma optimize( "", on ) void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit) { diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 09b502a72..3ca976b6e 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -166,6 +166,7 @@ static flag need_rewind = YES; static kdev_t current_tape_dev; static int extra_blocks_left = BLOCKS_BEYOND_EW; +static struct timer_list tp_timer; /* return_*_eof: * NO: not at EOF, @@ -1595,7 +1596,7 @@ static void end_dma(unsigned long * bytes_done) * has decided to do a long rewind, just when I didn't expect it. * Just try again. */ -static void qic02_tape_times_out(void) +static void qic02_tape_times_out(unsigned long dummy) { printk("time-out in %s driver\n", TPQIC02_NAME); if ((status_cmd_pending>0) || dma_mode) { @@ -1720,7 +1721,7 @@ static void qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) wake_up(&qic02_tape_transfer); } else { /* start next transfer, account for track-switching time */ - timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ; + mod_timer(&tp_timer, jiffies + 6*HZ); dma_transfer(); } } else { @@ -2940,8 +2941,8 @@ int __init qic02_tape_init(void) init_waitqueue_head(&qic02_tape_transfer); /* prepare timer */ TIMEROFF; - timer_table[QIC02_TAPE_TIMER].expires = 0; - timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out; + init_timer(&tp_timer); + tp_timer.function = qic02_tape_times_out; #ifndef CONFIG_QIC02_DYNCONF if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) @@ -2984,14 +2985,14 @@ void cleanup_module(void) qic02_release_resources(); } devfs_unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); - devfs_unregister(devfs_find_handle(NULL, "ntpqic11", 0, QIC02_TAPE_MAJOR, 2, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "tpqic11", 0, QIC02_TAPE_MAJOR, 3, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "ntpqic24", 0, QIC02_TAPE_MAJOR, 4, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "tpqic24", 0, QIC02_TAPE_MAJOR, 5, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "ntpqic120", 0, QIC02_TAPE_MAJOR, 6, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "tpqic120", 0, QIC02_TAPE_MAJOR, 7, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "ntpqic150", 0, QIC02_TAPE_MAJOR, 8, DEVFS_SPECIAL_CHR, 0)); - devfs_unregister(devfs_find_handle(NULL, "tpqic150", 0, QIC02_TAPE_MAJOR, 9, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic11", QIC02_TAPE_MAJOR, 2, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic11", QIC02_TAPE_MAJOR, 3, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic24", QIC02_TAPE_MAJOR, 4, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic24", QIC02_TAPE_MAJOR, 5, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic120", QIC02_TAPE_MAJOR, 6, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic120", QIC02_TAPE_MAJOR, 7, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "ntpqic150", QIC02_TAPE_MAJOR, 8, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "tpqic150", QIC02_TAPE_MAJOR, 9, DEVFS_SPECIAL_CHR, 0)); } int init_module(void) diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 4c0d29662..60a5c41f5 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -102,6 +102,7 @@ #ifdef CONFIG_VT extern void con_init_devfs (void); #endif +extern int rio_init(void); #define CONSOLE_DEV MKDEV(TTY_MAJOR,0) #define TTY_DEV MKDEV(TTYAUX_MAJOR,0) @@ -2029,7 +2030,7 @@ void tty_unregister_devfs (struct tty_driver *driver, unsigned minor) tty.driver = *driver; tty.device = MKDEV(driver->major, minor); - handle = devfs_find_handle (NULL, tty_name (&tty, buf), 0, + handle = devfs_find_handle (NULL, tty_name (&tty, buf), driver->major, minor, DEVFS_SPECIAL_CHR, 0); devfs_unregister (handle); diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 67ff8d856..920873d9e 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -469,9 +469,9 @@ void vcs_make_devfs (unsigned int index, int unregister) sprintf (name, "a%u", index + 1); if (unregister) { - devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0, 0, + devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0, DEVFS_SPECIAL_CHR, 0) ); - devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0, 0, + devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0, DEVFS_SPECIAL_CHR, 0) ); } else @@ -495,7 +495,7 @@ int __init vcs_init(void) if (error) printk("unable to get major %d for vcs device", VCS_MAJOR); - devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL); + devfs_handle = devfs_mk_dir (NULL, "vcc", NULL); devfs_register (devfs_handle, "0", DEVFS_FL_DEFAULT, VCS_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 4a3bfb859..5103433e1 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -330,6 +330,8 @@ static void videodev_proc_create(void) video_dev_proc_entry->owner = THIS_MODULE; } +#ifdef MODULE +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) static void videodev_proc_destroy(void) { if (video_dev_proc_entry != NULL) @@ -338,6 +340,8 @@ static void videodev_proc_destroy(void) if (video_proc_entry != NULL) remove_proc_entry("video", &proc_root); } +#endif +#endif static void videodev_proc_create_dev (struct video_device *vfd, char *name) { diff --git a/drivers/char/zr36120.c b/drivers/char/zr36120.c index a46c3d056..6e860021e 100644 --- a/drivers/char/zr36120.c +++ b/drivers/char/zr36120.c @@ -153,6 +153,7 @@ void __init handle_chipset(void) static void zoran_set_geo(struct zoran* ztv, struct vidinfo* i); +#if 0 /* unused */ static void zoran_dump(struct zoran *ztv) { @@ -169,6 +170,7 @@ void zoran_dump(struct zoran *ztv) p += sprintf(p, "%08x ",zrread(i)); } } +#endif /* unused */ static void reap_states(struct zoran* ztv) diff --git a/drivers/i2o/i2o_block.c b/drivers/i2o/i2o_block.c index 14281f55f..2dc69ae33 100644 --- a/drivers/i2o/i2o_block.c +++ b/drivers/i2o/i2o_block.c @@ -1664,6 +1664,7 @@ int i2o_block_init(void) if(i2o_install_handler(&i2o_block_handler)<0) { unregister_blkdev(MAJOR_NR, "i2o_block"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); printk(KERN_ERR "i2o_block: unable to register OSM.\n"); return -EINVAL; } @@ -1730,6 +1731,11 @@ void cleanup_module(void) if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) printk("i2o_block: cleanup_module failed\n"); + /* + * free request queue + */ + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + if(evt_running) { i = kill_proc(evt_pid, SIGTERM, 1); if(!i) { diff --git a/drivers/ide/hd.c b/drivers/ide/hd.c index 6fd109c83..698112b35 100644 --- a/drivers/ide/hd.c +++ b/drivers/ide/hd.c @@ -105,6 +105,24 @@ static int hd_sizes[MAX_HD<<6]; static int hd_blocksizes[MAX_HD<<6]; static int hd_hardsectsizes[MAX_HD<<6]; +static struct timer_list device_timer; + +#define SET_TIMER \ + do { \ + mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \ + } while (0) + +#define CLEAR_TIMER del_timer(&device_timer); + +#undef SET_INTR + +#define SET_INTR(x) \ +if ((DEVICE_INTR = (x)) != NULL) \ + SET_TIMER; \ +else \ + CLEAR_TIMER; + + #if (HD_DELAY > 0) unsigned long last_req; @@ -471,7 +489,7 @@ static void recal_intr(void) * This is another of the error-routines I don't know what to do with. The * best idea seems to just set reset, and start all over again. */ -static void hd_times_out(void) +static void hd_times_out(unsigned long dummy) { unsigned int dev; @@ -527,7 +545,7 @@ static void hd_request(void) if (DEVICE_INTR) return; repeat: - timer_active &= ~(1<<HD_TIMER); + del_timer(&device_timer); sti(); INIT_REQUEST; if (reset) { @@ -683,7 +701,7 @@ static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) void (*handler)(void) = DEVICE_INTR; DEVICE_INTR = NULL; - timer_active &= ~(1<<HD_TIMER); + del_timer(&device_timer); if (!handler) handler = unexpected_hd_interrupt; handler(); @@ -814,7 +832,8 @@ int __init hd_init(void) read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ hd_gendisk.next = gendisk_head; gendisk_head = &hd_gendisk; - timer_table[HD_TIMER].fn = hd_times_out; + init_timer(&device_timer); + device_timer.function = hd_times_out; hd_geninit(); return 0; } diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 53f6b5a9e..e8e67d549 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1768,6 +1768,9 @@ static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense) if (stat) toc->capacity = 0x1fffff; + HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); + drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + /* Remember that we've read this stuff. */ CDROM_STATE_FLAGS (drive)->toc_valid = 1; @@ -2066,11 +2069,11 @@ int ide_cdrom_get_last_session (struct cdrom_device_info *cdi, struct request_sense sense; int ret; - toc = info->toc; - if (!CDROM_STATE_FLAGS(drive)->toc_valid || toc == NULL) + if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL) if ((ret = cdrom_read_toc(drive, &sense))) return ret; + toc = info->toc; ms_info->addr.lba = toc->last_session_lba; ms_info->xa_flag = toc->xa_flag; @@ -2614,11 +2617,10 @@ static ide_module_t ide_cdrom_module = { /* options */ char *ignore = NULL; -#ifdef MODULE MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); -void __exit ide_cdrom_exit(void) +static void __exit ide_cdrom_exit(void) { ide_drive_t *drive; int failed = 0; @@ -2630,7 +2632,6 @@ void __exit ide_cdrom_exit(void) } ide_unregister_module (&ide_cdrom_module); } -#endif /* MODULE */ int ide_cdrom_init(void) { diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index dd5f660fe..168895fb5 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -580,6 +580,17 @@ static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match) #endif /* MAX_HWIFS > 1 */ /* + * init request queue + */ +static void ide_init_queue(ide_drive_t *drive) +{ + request_queue_t *q = &drive->queue; + + q->queuedata = HWGROUP(drive); + blk_init_queue(q, do_ide_request); +} + +/* * This routine sets up the irq for an ide interface, and creates a new * hwgroup for the irq/hwif if none was previously assigned. * @@ -677,6 +688,7 @@ static int init_irq (ide_hwif_t *hwif) hwgroup->drive = drive; drive->next = hwgroup->drive->next; hwgroup->drive->next = drive; + ide_init_queue(drive); } if (!hwgroup->hwif) { hwgroup->hwif = HWIF(hwgroup->drive); @@ -780,16 +792,13 @@ static void init_gendisk (ide_hwif_t *hwif) (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index, hwif->channel, unit, hwif->drives[unit].lun); hwif->drives[unit].de = - devfs_mk_dir (ide_devfs_handle, name, 0, NULL); + devfs_mk_dir (ide_devfs_handle, name, NULL); } } } static int hwif_init (ide_hwif_t *hwif) { - request_queue_t *q; - unsigned int unit; - if (!hwif->present) return 0; if (!hwif->irq) { @@ -840,12 +849,6 @@ static int hwif_init (ide_hwif_t *hwif) read_ahead[hwif->major] = 8; /* (4kB) */ hwif->present = 1; /* success */ - for (unit = 0; unit < MAX_DRIVES; ++unit) { - q = &hwif->drives[unit].queue; - q->queuedata = hwif->hwgroup; - blk_init_queue(q, do_ide_request); - } - #if (DEBUG_SPINLOCK > 0) { static int done = 0; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index b9aa05d4d..c9c3cb120 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -2023,6 +2023,7 @@ void ide_unregister (unsigned int index) drive->id = NULL; } drive->present = 0; + blk_cleanup_queue(&drive->queue); } if (d->present) hwgroup->drive = d; @@ -2048,7 +2049,6 @@ void ide_unregister (unsigned int index) kfree(blksize_size[hwif->major]); kfree(max_sectors[hwif->major]); kfree(max_readahead[hwif->major]); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(hwif->major)); blk_dev[hwif->major].data = NULL; blk_dev[hwif->major].queue = NULL; blksize_size[hwif->major] = NULL; @@ -3594,7 +3594,7 @@ int __init ide_init (void) if (!banner_printed) { printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n"); - ide_devfs_handle = devfs_mk_dir (NULL, "ide", 3, NULL); + ide_devfs_handle = devfs_mk_dir (NULL, "ide", NULL); system_bus_speed = ide_system_bus_speed(); banner_printed = 1; } diff --git a/drivers/ieee1394/Config.in b/drivers/ieee1394/Config.in index df0e3cfd2..bdb3002d2 100644 --- a/drivers/ieee1394/Config.in +++ b/drivers/ieee1394/Config.in @@ -4,18 +4,20 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then mainmenu_option next_comment comment 'IEEE 1394 (FireWire) support' - tristate 'IEEE 1394 (FireWire) support (EXPERIMENTAL)' CONFIG_IEEE1394 $CONFIG_PCI + dep_tristate 'IEEE 1394 (FireWire) support (EXPERIMENTAL)' CONFIG_IEEE1394 $CONFIG_PCI if [ "$CONFIG_IEEE1394" != "n" ]; then dep_tristate 'Texas Instruments PCILynx support' CONFIG_IEEE1394_PCILYNX $CONFIG_IEEE1394 if [ "$CONFIG_IEEE1394_PCILYNX" != "n" ]; then bool ' Use PCILynx local RAM' CONFIG_IEEE1394_PCILYNX_LOCALRAM + bool ' Support for non-IEEE1394 local ports' CONFIG_IEEE1394_PCILYNX_PORTS fi dep_tristate 'Adaptec AIC-5800 (AHA-89xx) support' CONFIG_IEEE1394_AIC5800 $CONFIG_IEEE1394 dep_tristate 'OHCI (Open Host Controller Interface) support' CONFIG_IEEE1394_OHCI1394 $CONFIG_IEEE1394 + dep_tristate 'Video1394 support' CONFIG_IEEE1394_VIDEO1394 $CONFIG_IEEE1394_OHCI1394 dep_tristate 'Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394 diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile index c89374f45..cc6b40ee4 100644 --- a/drivers/ieee1394/Makefile +++ b/drivers/ieee1394/Makefile @@ -53,13 +53,20 @@ else endif ifeq ($(CONFIG_IEEE1394_OHCI1394),y) -L_OBJS += ohci1394.o +LX_OBJS += ohci1394.o else ifeq ($(CONFIG_IEEE1394_OHCI1394),m) - M_OBJS += ohci1394.o + MX_OBJS += ohci1394.o endif endif +ifeq ($(CONFIG_IEEE1394_VIDEO1394),y) +L_OBJS += video1394.o +else + ifeq ($(CONFIG_IEEE1394_VIDEO1394),m) + M_OBJS += video1394.o + endif +endif ifeq ($(CONFIG_IEEE1394_RAWIO),y) L_OBJS += raw1394.o diff --git a/drivers/ieee1394/aic5800.c b/drivers/ieee1394/aic5800.c index 6cf3779d5..2748c21b6 100644 --- a/drivers/ieee1394/aic5800.c +++ b/drivers/ieee1394/aic5800.c @@ -646,7 +646,7 @@ static void aic_irq_handler(int irq, void *dev_id, struct pt_regs *regs) phyid = phyid & 0x3F; handle_selfid(aic, host, phyid, isroot, rcv_bytes); } else { - hpsb_packet_received(host, aic->rcv_page, rcv_bytes); + hpsb_packet_received(host, aic->rcv_page, rcv_bytes, 0); }; } else { PRINT(KERN_ERR, aic->id, diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c index a884f2a1c..486ad1ad4 100644 --- a/drivers/ieee1394/csr.c +++ b/drivers/ieee1394/csr.c @@ -4,6 +4,9 @@ * CSR implementation, iso/bus manager implementation. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/string.h> diff --git a/drivers/ieee1394/guid.c b/drivers/ieee1394/guid.c index 36ac332c5..1aa453292 100644 --- a/drivers/ieee1394/guid.c +++ b/drivers/ieee1394/guid.c @@ -4,6 +4,9 @@ * GUID collection and management * * Copyright (C) 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/kernel.h> diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c index 3e20824aa..130a40527 100644 --- a/drivers/ieee1394/highlevel.c +++ b/drivers/ieee1394/highlevel.c @@ -2,6 +2,9 @@ * IEEE 1394 for Linux * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 9a9951204..8e0e80da1 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -5,6 +5,9 @@ * * Copyright (C) 1999 Andreas E. Bombe * Copyright (C) 1999 Emanuel Pirker + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index ffcfc7f52..e0641271b 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -5,6 +5,9 @@ * highlevel or lowlevel code * * Copyright (C) 1999, 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> @@ -534,7 +537,7 @@ struct hpsb_packet *create_reply_packet(struct hpsb_host *host, quadlet_t *data, if (packet == NULL) break void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, - size_t size) + size_t size, int write_acked) { struct hpsb_packet *packet; int length, rcode, extcode; @@ -548,7 +551,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_write(host, source, data+3, addr, 4); - if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) { + if (!write_acked + && ((data[0] >> 16) & NODE_MASK) != NODE_MASK) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); fill_async_write_resp(packet, rcode); @@ -561,7 +565,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, rcode = highlevel_write(host, source, data+4, addr, data[3]>>16); - if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) { + if (!write_acked + && ((data[0] >> 16) & NODE_MASK) != NODE_MASK) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); fill_async_write_resp(packet, rcode); @@ -644,7 +649,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, #undef PREP_REPLY_PACKET -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size) +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked) { int tcode; @@ -672,7 +678,7 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size) case TCODE_READQ: case TCODE_READB: case TCODE_LOCK_REQUEST: - handle_incoming_packet(host, tcode, data, size); + handle_incoming_packet(host, tcode, data, size, write_acked); break; diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h index 636aef40e..faeeca45d 100644 --- a/drivers/ieee1394/ieee1394_core.h +++ b/drivers/ieee1394/ieee1394_core.h @@ -52,8 +52,7 @@ struct hpsb_packet { * overwritten to allow in-place byte swapping. Neither of these is * CRCed (the sizes also don't include CRC), but contain space for at * least one additional quadlet to allow in-place CRCing. The memory is - * also guaranteed to have physical mapping (virt_to_bus() is meaningful - * on these pointers). + * also guaranteed to be DMA mappable. */ quadlet_t *header; quadlet_t *data; @@ -145,7 +144,12 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, * immediately), with the header (i.e. the first four quadlets) in machine byte * order and the data block in big endian. *data can be safely overwritten * after this call. + * + * If the packet is a write request, write_acked is to be set to true if it was + * ack_complete'd already, false otherwise. This arg is ignored for any other + * packet type. */ -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size); +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked); #endif /* _IEEE1394_CORE_H */ diff --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c index c1ebadd10..39a61a01b 100644 --- a/drivers/ieee1394/ieee1394_syms.c +++ b/drivers/ieee1394/ieee1394_syms.c @@ -4,6 +4,9 @@ * Exported symbols for module usage. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/types.h> diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index c5c5cc4ad..179834ac7 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -4,6 +4,9 @@ * Transaction support. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/sched.h> @@ -152,38 +155,58 @@ void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, * Return value: The allocated transaction label or -1 if there was no free * tlabel and @wait is false. */ +static int __get_tlabel(struct hpsb_host *host, nodeid_t nodeid) +{ + int tlabel; + + if (host->tlabel_count) { + host->tlabel_count--; + + if (host->tlabel_pool[0] != ~0) { + tlabel = ffz(host->tlabel_pool[0]); + host->tlabel_pool[0] |= 1 << tlabel; + } else { + tlabel = ffz(host->tlabel_pool[1]); + host->tlabel_pool[1] |= 1 << tlabel; + tlabel += 32; + } + return tlabel; + } + return -1; +} + int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait) { unsigned long flags; int tlabel; - - while (1) { - spin_lock_irqsave(&host->tlabel_lock, flags); - - if (host->tlabel_count) { - host->tlabel_count--; - - if (host->tlabel_pool[0] != ~0) { - tlabel = ffz(host->tlabel_pool[0]); - host->tlabel_pool[0] |= 1 << tlabel; - } else { - tlabel = ffz(host->tlabel_pool[1]); - host->tlabel_pool[1] |= 1 << tlabel; - tlabel += 32; - } - - spin_unlock_irqrestore(&host->tlabel_lock, flags); - return tlabel; - } - - spin_unlock_irqrestore(&host->tlabel_lock, flags); - - if (wait) { - sleep_on(&host->tlabel_wait); - } else { - return -1; - } - } + wait_queue_t wq; + + spin_lock_irqsave(&host->tlabel_lock, flags); + + tlabel = __get_tlabel(host, nodeid); + if (tlabel != -1 || !wait) { + spin_unlock_irqrestore(&host->tlabel_lock, flags); + return tlabel; + } + + init_waitqueue_entry(&wq, current); + add_wait_queue(&host->tlabel_wait, &wq); + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + tlabel = __get_tlabel(host, nodeid); + if (tlabel != -1) break; + + spin_unlock_irqrestore(&host->tlabel_lock, flags); + schedule(); + spin_lock_irqsave(&host->tlabel_lock, flags); + } + + spin_unlock_irqrestore(&host->tlabel_lock, flags); + set_current_state(TASK_RUNNING); + remove_wait_queue(&host->tlabel_wait, &wq); + + return tlabel; } /** diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h index 411b79b36..84be6aa05 100644 --- a/drivers/ieee1394/ieee1394_types.h +++ b/drivers/ieee1394/ieee1394_types.h @@ -11,15 +11,23 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include <linux/wait.h> #define DECLARE_WAITQUEUE(name, task) struct wait_queue name = { task, NULL } typedef struct wait_queue *wait_queue_head_t; +typedef struct wait_queue wait_queue_t; inline static void init_waitqueue_head(wait_queue_head_t *wh) { *wh = NULL; } +inline static void init_waitqueue_entry(wait_queue_t *wq, struct task_struct *p) +{ + wq->task = p; + wq->next = NULL; +} + static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); @@ -30,6 +38,22 @@ static __inline__ void list_add_tail(struct list_head *new, struct list_head *he #define set_current_state(state_value) \ do { current->state = (state_value); } while (0) + +#include <asm/page.h> +/* Pure 2^n version of get_order */ +extern __inline__ int get_order(unsigned long size) +{ + int order; + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + + #include <linux/pci.h> inline static int pci_enable_device(struct pci_dev *dev) { @@ -39,8 +63,134 @@ inline static int pci_enable_device(struct pci_dev *dev) return 0; } +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 +#define PCI_ROM_RESOURCE 6 +#define pci_resource_start(dev, bar) ((bar) == PCI_ROM_RESOURCE \ + ? (dev)->rom_address \ + : (dev)->base_address[(bar)]) +#define BUG() *(int *)0 = 0 + +#include <asm/io.h> +typedef u32 dma_addr_t; + +extern inline int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) +{ + return 1; +} + +extern inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)); + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +extern inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +extern inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + return virt_to_bus(ptr); +} + +extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +struct scatterlist {}; +extern inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + return nents; +} + +extern inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +extern inline void pci_dma_sync_single(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +extern inline void pci_dma_sync_sg(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + + +#ifndef _LINUX_DEVFS_FS_KERNEL_H +typedef struct devfs_entry * devfs_handle_t; +#define DEVFS_FL_NONE 0 + +static inline devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, + unsigned int flags, + unsigned int major, + unsigned int minor, + umode_t mode, + void *ops, void *info) +{ + return NULL; +} +static inline void devfs_unregister (devfs_handle_t de) +{ + return; +} +static inline int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops) +{ + return register_chrdev (major, name, fops); +} +static inline int devfs_unregister_chrdev (unsigned int major,const char *name) +{ + return unregister_chrdev (major, name); +} +#endif /* _LINUX_DEVFS_FS_KERNEL_H */ + + +#define V22_COMPAT_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define V22_COMPAT_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#define OWNER_THIS_MODULE + +#else /* Linux version < 2.3 */ + +#define V22_COMPAT_MOD_INC_USE_COUNT do {} while (0) +#define V22_COMPAT_MOD_DEC_USE_COUNT do {} while (0) +#define OWNER_THIS_MODULE owner: THIS_MODULE, + #endif /* Linux version < 2.3 */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,18) #include <asm/spinlock.h> #else @@ -55,9 +205,9 @@ inline static int pci_enable_device(struct pci_dev *dev) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif -typedef __u32 quadlet_t; -typedef __u64 octlet_t; -typedef __u16 nodeid_t; +typedef u32 quadlet_t; +typedef u64 octlet_t; +typedef u16 nodeid_t; #define BUS_MASK 0xffc0 #define NODE_MASK 0x003f diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index a0933c3e4..05e1063d7 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -25,10 +25,10 @@ * . Async Request Receive * . Async Response Transmit * . Iso Receive + * . DMA mmap for iso receive * * Things not implemented: * . Iso Transmit - * . DMA to user's space in iso receive mode * . DMA error recovery * * Things to be fixed: @@ -38,7 +38,6 @@ * . Self-id are sometimes not received properly * if card is initialized with no other nodes * on the bus - * . SONY CXD3222 chip is not working properly * . Apple PowerBook detected but not working yet */ @@ -56,7 +55,7 @@ * Albrecht Dress <ad@mpifr-bonn.mpg.de> * . Apple PowerBook detection * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> - * . Reset the board properly before leaving + * . Reset the board properly before leaving + misc cleanups */ #include <linux/config.h> @@ -130,6 +129,8 @@ int supported_chips[][2] = { { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72870 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72871 }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW }, + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_ALI_OHCI1394_M5251 }, + { PCI_VENDOR_ID_LUCENT, PCI_DEVICE_ID_LUCENT_FW323 }, { -1, -1 } }; @@ -142,617 +143,6 @@ static int init_driver(void); static void dma_trm_bh(void *data); static void dma_rcv_bh(void *data); static void dma_trm_reset(struct dma_trm_ctx *d); -static void stop_context(struct ti_ohci *ohci, int reg, char *msg); - -#ifdef _VIDEO_1394_H - -/* Taken from bttv.c */ -/*******************************/ -/* Memory management functions */ -/*******************************/ - -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guarenteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) - ret = (pte_page(pte)|(adr&(PAGE_SIZE-1))); - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long kvirt_to_bus(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - -static void * rvmalloc(unsigned long size) -{ - void * mem; - unsigned long adr, page; - - mem=vmalloc(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_reserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr, page; - - if (mem) - { - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_unreserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } -} - -static int free_dma_fbuf_ctx(struct dma_fbuf_ctx **d) -{ - int i; - struct ti_ohci *ohci; - - if ((*d)==NULL) return -1; - - ohci = (struct ti_ohci *)(*d)->ohci; - - DBGMSG(ohci->id, "Freeing dma_fbuf_ctx %d", (*d)->ctx); - - stop_context(ohci, (*d)->ctrlClear, NULL); - - if ((*d)->buf) rvfree((void *)(*d)->buf, - (*d)->num_desc * (*d)->buf_size); - - if ((*d)->prg) { - for (i=0;i<(*d)->num_desc;i++) - if ((*d)->prg[i]) kfree((*d)->prg[i]); - kfree((*d)->prg); - } - - if ((*d)->buffer_status) - kfree((*d)->buffer_status); - - kfree(*d); - *d = NULL; - - return 0; -} - -static struct dma_fbuf_ctx * -alloc_dma_fbuf_ctx(struct ti_ohci *ohci, int ctx, int num_desc, - int buf_size, int channel) -{ - struct dma_fbuf_ctx *d=NULL; - int i; - - d = (struct dma_fbuf_ctx *)kmalloc(sizeof(struct dma_fbuf_ctx), - GFP_KERNEL); - - if (d==NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma_fbuf_ctx"); - return NULL; - } - - d->ohci = (void *)ohci; - d->ctx = ctx; - d->channel = channel; - d->num_desc = num_desc; - d->frame_size = buf_size; - if (buf_size%PAGE_SIZE) - d->buf_size = buf_size + PAGE_SIZE - (buf_size%PAGE_SIZE); - else - d->buf_size = buf_size; - d->ctrlSet = OHCI1394_IrRcvContextControlSet+32*d->ctx; - d->ctrlClear = OHCI1394_IrRcvContextControlClear+32*d->ctx; - d->cmdPtr = OHCI1394_IrRcvCommandPtr+32*d->ctx; - d->ctxMatch = OHCI1394_IrRcvContextMatch+32*d->ctx; - d->nb_cmd = d->buf_size / PAGE_SIZE + 1; - d->last_buffer = 0; - d->buf = NULL; - d->prg = NULL; - init_waitqueue_head(&d->waitq); - - d->buf = rvmalloc(d->num_desc * d->buf_size); - - if (d->buf == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuffer"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->buf, 0, d->num_desc * d->buf_size); - - d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *), - GFP_KERNEL); - - if (d->prg == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd *)); - - for (i=0;i<d->num_desc;i++) { - d->prg[i] = kmalloc(d->nb_cmd * sizeof(struct dma_cmd), - GFP_KERNEL); - if (d->prg[i] == NULL) { - PRINT(KERN_ERR, ohci->id, - "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - } - - d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), - GFP_KERNEL); - - if (d->buffer_status == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); - - PRINT(KERN_INFO, ohci->id, "Iso DMA to User's Space: %d buffers " - "of size %d allocated for a frame size %d, each with %d prgs", - d->num_desc, d->buf_size, d->frame_size, d->nb_cmd); - - return d; -} - -static void initialize_dma_fbuf_prg(struct dma_cmd *prg, int n, - int frame_size, unsigned long buf) -{ - int i; - int leftsize = (frame_size%PAGE_SIZE) ? - frame_size%PAGE_SIZE : PAGE_SIZE; - - /* the first descriptor will sync and read only 4 bytes */ - prg[0].control = (0x280F << 16) | 4; - prg[0].address = kvirt_to_bus(buf); - prg[0].branchAddress = (virt_to_bus(&(prg[1].control)) - & 0xfffffff0) | 0x1; - prg[0].status = 0; - - /* the second descriptor will read PAGE_SIZE-4 bytes */ - prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); - prg[1].address = kvirt_to_bus(buf+4); - prg[1].branchAddress = (virt_to_bus(&(prg[2].control)) - & 0xfffffff0) | 0x1; - prg[1].status = 0; - - for (i=2;i<n-1;i++) { - prg[i].control = (0x280C << 16) | PAGE_SIZE; - prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); - - prg[i].branchAddress = - (virt_to_bus(&(prg[i+1].control)) - & 0xfffffff0) | 0x1; - - prg[i].status = 0; - } - - /* the last descriptor will generate an interrupt */ - prg[i].control = (0x283C << 16) | leftsize; - prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); - prg[i].status = 0; -} - -static void initialize_dma_fbuf_ctx(struct dma_fbuf_ctx *d, int tag) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;i<d->num_desc;i++) { - initialize_dma_fbuf_prg(d->prg[i], d->nb_cmd, d->frame_size, - (unsigned long)d->buf+i*d->buf_size); - } - - /* Set bufferFill, no header */ - reg_write(ohci, d->ctrlSet, 0x80000000); - - /* Set the context match register to match on all tags, - sync for sync tag, and listen to d->channel */ - reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); -} - -/* find which context is listening to this channel */ -int fbuf_ctx_listening(struct ti_ohci *ohci, int channel) -{ - int i; - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]) { - if (ohci->fbuf_context[i]->channel==channel) - return i; - } - - PRINT(KERN_ERR, ohci->id, - "no iso context is listening to channel %d", - channel); - return -1; -} - -static int ohci_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - - switch(cmd) - { - case VIDEO1394_LISTEN_CHANNEL: - { - struct video1394_mmap v; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { - PRINT(KERN_ERR, ohci->id, - "iso channel %d out of bound", v.channel); - return -EFAULT; - } - if (test_and_set_bit(v.channel, &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is already taken", v.channel); - return -EFAULT; - } - - /* find a free iso context */ - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]==NULL) break; - - if (i==(ohci->nb_iso_ctx-1)) { - PRINT(KERN_ERR, ohci->id, "no iso context available"); - return -EFAULT; - } - - if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->id, - "%d buffers of size %d bytes is too big", - v.nb_buffers, v.buf_size); - return -EFAULT; - } - - ohci->fbuf_context[i] = - alloc_dma_fbuf_ctx(ohci, i+1, v.nb_buffers, - v.buf_size, v.channel); - - if (ohci->fbuf_context[i] == NULL) { - PRINT(KERN_ERR, ohci->id, - "Couldn't allocate fbuf context"); - return -EFAULT; - } - initialize_dma_fbuf_ctx(ohci->fbuf_context[i], v.sync_tag); - - ohci->current_fbuf_ctx = ohci->fbuf_context[i]; - - v.buf_size = ohci->fbuf_context[i]->buf_size; - - PRINT(KERN_INFO, ohci->id, - "iso context %d listen on channel %d", i+1, - v.channel); - - if(copy_to_user((void *)arg, &v, sizeof(v))) - return -EFAULT; - - return 0; - } - case VIDEO1394_UNLISTEN_CHANNEL: - { - int channel; - int i; - - if(copy_from_user(&channel, (void *)arg, sizeof(int))) - return -EFAULT; - - if (!test_and_clear_bit(channel, &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is not being used", channel); - return -EFAULT; - } - - i = fbuf_ctx_listening(ohci, channel); - if (i<0) return -EFAULT; - - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - - PRINT(KERN_INFO, ohci->id, - "iso context %d stop listening on channel %d", - i+1, channel); - - return 0; - } - case VIDEO1394_QUEUE_BUFFER: - { - struct video1394_wait v; - struct dma_fbuf_ctx *d; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - - i = fbuf_ctx_listening(ohci, v.channel); - if (i<0) return -EFAULT; - d = ohci->fbuf_context[i]; - - if ((v.buffer<0) || (v.buffer>d->num_desc)) { - PRINT(KERN_ERR, ohci->id, - "buffer %d out of range",v.buffer); - return -EFAULT; - } - - if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { - PRINT(KERN_ERR, ohci->id, - "buffer %d is already used",v.buffer); - return -EFAULT; - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - d->prg[d->last_buffer][d->nb_cmd-1].branchAddress = - (virt_to_bus(&(d->prg[v.buffer][0].control)) - & 0xfffffff0) | 0x1; - - d->last_buffer = v.buffer; - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[v.buffer][0])) | 0x1 ); - - /* Run IR context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - PRINT(KERN_INFO, ohci->id, - "Waking up iso dma ctx=%d", d->ctx); - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - return 0; - - } - case VIDEO1394_WAIT_BUFFER: - { - struct video1394_wait v; - struct dma_fbuf_ctx *d; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - - i = fbuf_ctx_listening(ohci, v.channel); - if (i<0) return -EFAULT; - d = ohci->fbuf_context[i]; - - if ((v.buffer<0) || (v.buffer>d->num_desc)) { - PRINT(KERN_ERR, ohci->id, - "buffer %d out of range",v.buffer); - return -EFAULT; - } - - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - case VIDEO1394_BUFFER_QUEUED: - while(d->buffer_status[v.buffer]!= - VIDEO1394_BUFFER_READY) { - interruptible_sleep_on(&d->waitq); - if(signal_pending(current)) return -EINTR; - } - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - default: - PRINT(KERN_ERR, ohci->id, - "buffer %d is not queued",v.buffer); - return -EFAULT; - } - } - default: - return -EINVAL; - } -} - -/* - * This maps the vmalloced and reserved fbuffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - remap_page_range is kind of inefficient for page by page remapping. - * But e.g. pte_alloc() does not work in modules ... :-( - */ - -static int do_fbuf_mmap(struct ti_ohci *ohci, struct dma_fbuf_ctx *d, - const char *adr, unsigned long size) -{ - unsigned long start=(unsigned long) adr; - unsigned long page,pos; - - if (size>d->num_desc * d->buf_size) { - PRINT(KERN_ERR, ohci->id, - "fbuf context %d buf size is different from mmap size", - d->ctx); - return -EINVAL; - } - if (!d->buf) { - PRINT(KERN_ERR, ohci->id, - "fbuf context %d is not allocated", d->ctx); - return -EINVAL; - } - - pos=(unsigned long) d->buf; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - size-=PAGE_SIZE; - } - return 0; -} - -int ohci_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ti_ohci *ohci=&cards[MINOR(file->f_dentry->d_inode->i_rdev)]; - PRINT(KERN_INFO, ohci->id, "mmap"); - if (ohci->current_fbuf_ctx == NULL) { - PRINT(KERN_ERR, ohci->id, "current fbuf context not set"); - return -EINVAL; - } - - return do_fbuf_mmap(ohci, ohci->current_fbuf_ctx, - (char *)vma->vm_start, - (unsigned long)(vma->vm_end-vma->vm_start)); - return 0; -} - -static int ohci_open(struct inode *inode, struct file *file) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - PRINT(KERN_INFO, ohci->id, "open"); - return 0; -} - -static int ohci_release(struct inode *inode, struct file *file) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - int i; - - PRINT(KERN_INFO, ohci->id, "release"); - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]) { - if (!test_and_clear_bit(ohci->fbuf_context[i]->channel, - &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is not being used", - ohci->fbuf_context[i]->channel); - } - PRINT(KERN_INFO, ohci->id, - "iso context %d stop listening on channel %d", - i+1, ohci->fbuf_context[i]->channel); - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - } - return 0; -} - -static struct file_operations ohci_fops= -{ - owner: THIS_MODULE, - ioctl: ohci_ioctl, - mmap: ohci_mmap, - open: ohci_open, - release: ohci_release -}; - -int wakeup_dma_fbuf_ctx(struct ti_ohci *ohci, struct dma_fbuf_ctx *d) -{ - int i; - - if (d==NULL) { - PRINT(KERN_ERR, ohci->id, "Iso receive event received but " - "context not allocated"); - return -EFAULT; - } - - for (i=0;i<d->num_desc;i++) { - if (d->prg[i][d->nb_cmd-1].status) { - d->prg[i][d->nb_cmd-1].status=0; - d->buffer_status[i] = VIDEO1394_BUFFER_READY; - } - } - if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); - return 0; -} - -#endif - - /*********************************** * IEEE-1394 functionality section * @@ -827,14 +217,14 @@ static int set_phy_reg(struct ti_ohci *ohci, int addr, unsigned char data) { inline static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, int phyid, int isroot) { - quadlet_t *q = ohci->self_id_buffer; + quadlet_t *q = ohci->selfid_buf_cpu; quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount); size_t size; quadlet_t lsid; /* Self-id handling seems much easier than for the aic5800 chip. All the self-id packets, including this device own self-id, - should be correctly arranged in the self_id_buffer at this + should be correctly arranged in the selfid buffer at this stage */ /* Check status of self-id reception */ @@ -952,55 +342,36 @@ static int run_context(struct ti_ohci *ohci, int reg, char *msg) return 0; } -static void stop_context(struct ti_ohci *ohci, int reg, char *msg) -{ - int i=0; - - /* stop the channel program if it's still running */ - reg_write(ohci, reg, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, reg) & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, ohci->id, - "runaway loop while stopping context..."); - break; - } - } - if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg); -} - /* Generate the dma receive prgs and start the context */ static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d) { struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); int i; - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); for (i=0; i<d->num_desc; i++) { /* end of descriptor list? */ if ((i+1) < d->num_desc) { - d->prg[i]->control = (0x283C << 16) | d->buf_size; - d->prg[i]->branchAddress = - (virt_to_bus(d->prg[i+1]) & 0xfffffff0) | 0x1; + d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; + d->prg_cpu[i]->branchAddress = + (d->prg_bus[i+1] & 0xfffffff0) | 0x1; } else { - d->prg[i]->control = (0x283C << 16) | d->buf_size; - d->prg[i]->branchAddress = - (virt_to_bus(d->prg[0]) & 0xfffffff0); + d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; + d->prg_cpu[i]->branchAddress = + d->prg_bus[0] & 0xfffffff0; } - d->prg[i]->address = virt_to_bus(d->buf[i]); - d->prg[i]->status = d->buf_size; + d->prg_cpu[i]->address = d->buf_bus[i]; + d->prg_cpu[i]->status = d->buf_size; } d->buf_ind = 0; d->buf_offset = 0; /* Tell the controller where the first AR program is */ - reg_write(ohci, d->cmdPtr, virt_to_bus(d->prg[0]) | 0x1); + reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); /* Run AR context */ reg_write(ohci, d->ctrlSet, 0x00008000); @@ -1014,26 +385,30 @@ static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); /* Stop the context */ - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); d->prg_ind = 0; d->sent_ind = 0; d->free_prgs = d->num_desc; d->branchAddrPtr = NULL; - d->first = NULL; - d->last = NULL; + d->fifo_first = NULL; + d->fifo_last = NULL; + d->pending_first = NULL; + d->pending_last = NULL; PRINT(KERN_INFO, ohci->id, "AT dma ctx=%d initialized", d->ctx); } /* Count the number of available iso contexts */ -static int get_nb_iso_ctx(struct ti_ohci *ohci) +static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) { int i,ctx=0; u32 tmp; - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0xffffffff); - tmp = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); + reg_write(ohci, reg, 0xffffffff); + tmp = reg_read(ohci, reg); + + DBGMSG(ohci->id,"Iso contexts reg: %08x implemented: %08x", reg, tmp); /* Count the number of contexts */ for(i=0; i<32; i++) { @@ -1062,7 +437,7 @@ static int ohci_initialize(struct hpsb_host *host) if ((retval=ohci_soft_reset(ohci))<0) return retval; /* - *Delay aftger soft reset to make sure everything has settled + * Delay after soft reset to make sure everything has settled * down (sanity) */ mdelay(100); @@ -1089,31 +464,33 @@ static int ohci_initialize(struct hpsb_host *host) reg_write(ohci, OHCI1394_LinkControlSet, 0x00300000); /* Clear interrupt registers */ - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); /* Set up self-id dma buffer */ - reg_write(ohci, OHCI1394_SelfIDBuffer, - virt_to_bus(ohci->self_id_buffer)); + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); /* enable self-id dma */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200); /* Set the configuration ROM mapping register */ - reg_write(ohci, OHCI1394_ConfigROMmap, - virt_to_bus(ohci->csr_config_rom)); - - /* Write the config ROM header */ - reg_write(ohci, OHCI1394_ConfigROMhdr, - cpu_to_be32(ohci->csr_config_rom[0])); + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); /* Set bus options */ reg_write(ohci, OHCI1394_BusOptions, - cpu_to_be32(ohci->csr_config_rom[2])); - + cpu_to_be32(ohci->csr_config_rom_cpu[2])); + +#if 0 /* Write the GUID into the csr config rom */ - ohci->csr_config_rom[3] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi)); - ohci->csr_config_rom[4] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo)); + ohci->csr_config_rom_cpu[3] = + be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi)); + ohci->csr_config_rom_cpu[4] = + be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo)); +#endif + + /* Write the config ROM header */ + reg_write(ohci, OHCI1394_ConfigROMhdr, + cpu_to_be32(ohci->csr_config_rom_cpu[0])); ohci->max_packet_size = 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); @@ -1124,39 +501,56 @@ static int ohci_initialize(struct hpsb_host *host) reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); /* Initialize IR dma */ - ohci->nb_iso_ctx = get_nb_iso_ctx(ohci); - PRINT(KERN_INFO, ohci->id, "%d iso contexts available", - ohci->nb_iso_ctx); - for (i=0;i<ohci->nb_iso_ctx;i++) { - reg_write(ohci, OHCI1394_IrRcvContextControlClear+32*i, + ohci->nb_iso_rcv_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); + PRINT(KERN_INFO, ohci->id, "%d iso receive contexts available", + ohci->nb_iso_rcv_ctx); + for (i=0;i<ohci->nb_iso_rcv_ctx;i++) { + reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i, 0xffffffff); - reg_write(ohci, OHCI1394_IrRcvContextMatch+32*i, 0); - reg_write(ohci, OHCI1394_IrRcvCommandPtr+32*i, 0); - } -#ifdef _VIDEO_1394_H - ohci->fbuf_context = (struct dma_fbuf_ctx **) - kmalloc((ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *), - GFP_KERNEL); - if (ohci->fbuf_context) - memset(ohci->fbuf_context, 0, - (ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *)); - else { - PRINT(KERN_ERR, ohci->id, "Cannot allocate fbuf_context"); - return -1; + reg_write(ohci, OHCI1394_IsoRcvContextMatch+32*i, 0); + reg_write(ohci, OHCI1394_IsoRcvCommandPtr+32*i, 0); } -#endif + /* Set bufferFill, isochHeader, multichannel for IR context */ - reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000); + reg_write(ohci, OHCI1394_IsoRcvContextControlSet, 0xd0000000); /* Set the context match register to match on all tags */ - reg_write(ohci, OHCI1394_IrRcvContextMatch, 0xf0000000); + reg_write(ohci, OHCI1394_IsoRcvContextMatch, 0xf0000000); + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); + + /* Initialize IT dma */ + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + PRINT(KERN_INFO, ohci->id, "%d iso transmit contexts available", + ohci->nb_iso_xmit_ctx); + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) { + reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i, + 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitCommandPtr+32*i, 0); + } + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); /* Clear the multi channel mask high and low registers */ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + /* Initialize AR dma */ + initialize_dma_rcv_ctx(ohci->ar_req_context); + initialize_dma_rcv_ctx(ohci->ar_resp_context); + + /* Initialize AT dma */ + initialize_dma_trm_ctx(ohci->at_req_context); + initialize_dma_trm_ctx(ohci->at_resp_context); + + /* Initialize IR dma */ + initialize_dma_rcv_ctx(ohci->ir_context); /* Set up isoRecvIntMask to generate interrupts for context 0 (thanks to Michael Greger for seeing that I forgot this) */ @@ -1193,23 +587,13 @@ static int ohci_initialize(struct hpsb_host *host) OHCI1394_ARRQ | OHCI1394_respTxComplete | OHCI1394_reqTxComplete | - OHCI1394_isochRx + OHCI1394_isochRx | + OHCI1394_isochTx ); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); - /* Initialize AR dma */ - initialize_dma_rcv_ctx(ohci->ar_req_context); - initialize_dma_rcv_ctx(ohci->ar_resp_context); - - /* Initialize AT dma */ - initialize_dma_trm_ctx(ohci->at_req_context); - initialize_dma_trm_ctx(ohci->at_resp_context); - - /* Initialize IR dma */ - initialize_dma_rcv_ctx(ohci->ir_context); - return 1; } @@ -1223,74 +607,132 @@ static void ohci_remove(struct hpsb_host *host) } } -/* Insert a packet in the AT DMA fifo and generate the DMA prg */ +/* + * Insert a packet in the AT DMA fifo and generate the DMA prg + * FIXME: rewrite the program in order to accept packets crossing + * page boundaries. + * check also that a single dma descriptor doesn't cross a + * page boundary. + */ static void insert_packet(struct ti_ohci *ohci, struct dma_trm_ctx *d, struct hpsb_packet *packet) { u32 cycleTimer; int idx = d->prg_ind; - d->prg[idx].begin.address = 0; - d->prg[idx].begin.branchAddress = 0; + d->prg_cpu[idx]->begin.address = 0; + d->prg_cpu[idx]->begin.branchAddress = 0; if (d->ctx==1) { /* * For response packets, we need to put a timeout value in * the 16 lower bits of the status... let's try 1 sec timeout */ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - d->prg[idx].begin.status = + d->prg_cpu[idx]->begin.status = (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | ((cycleTimer&0x01fff000)>>12); DBGMSG(ohci->id, "cycleTimer: %08x timeStamp: %08x", - cycleTimer, d->prg[idx].begin.status); + cycleTimer, d->prg_cpu[idx]->begin.status); } else - d->prg[idx].begin.status = 0; + d->prg_cpu[idx]->begin.status = 0; - d->prg[idx].data[0] = packet->speed_code<<16 | + d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | (packet->header[0] & 0xFFFF); - d->prg[idx].data[1] = (packet->header[1] & 0xFFFF) | + d->prg_cpu[idx]->data[1] = (packet->header[1] & 0xFFFF) | (packet->header[0] & 0xFFFF0000); - d->prg[idx].data[2] = packet->header[2]; - d->prg[idx].data[3] = packet->header[3]; + d->prg_cpu[idx]->data[2] = packet->header[2]; + d->prg_cpu[idx]->data[3] = packet->header[3]; if (packet->data_size) { /* block transmit */ - d->prg[idx].begin.control = OUTPUT_MORE_IMMEDIATE | 0x10; - d->prg[idx].end.control = OUTPUT_LAST | packet->data_size; - d->prg[idx].end.address = virt_to_bus(packet->data); - d->prg[idx].end.branchAddress = 0; - d->prg[idx].end.status = 0x4000; + d->prg_cpu[idx]->begin.control = OUTPUT_MORE_IMMEDIATE | 0x10; + d->prg_cpu[idx]->end.control = OUTPUT_LAST | packet->data_size; + /* + * FIXME: check that the packet data buffer + * do not cross a page boundary + */ + d->prg_cpu[idx]->end.address = + pci_map_single(ohci->dev, packet->data, + packet->data_size, PCI_DMA_TODEVICE); + d->prg_cpu[idx]->end.branchAddress = 0; + d->prg_cpu[idx]->end.status = 0; if (d->branchAddrPtr) - *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x3; - d->branchAddrPtr = &(d->prg[idx].end.branchAddress); + *(d->branchAddrPtr) = d->prg_bus[idx] | 0x3; + d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); } else { /* quadlet transmit */ - d->prg[idx].begin.control = + d->prg_cpu[idx]->begin.control = OUTPUT_LAST_IMMEDIATE | packet->header_size; if (d->branchAddrPtr) - *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x2; - d->branchAddrPtr = &(d->prg[idx].begin.branchAddress); + *(d->branchAddrPtr) = d->prg_bus[idx] | 0x2; + d->branchAddrPtr = &(d->prg_cpu[idx]->begin.branchAddress); } d->free_prgs--; /* queue the packet in the appropriate context queue */ - if (d->last) { - d->last->xnext = packet; - d->last = packet; + if (d->fifo_last) { + d->fifo_last->xnext = packet; + d->fifo_last = packet; + } + else { + d->fifo_first = packet; + d->fifo_last = packet; + } + d->prg_ind = (d->prg_ind+1)%d->num_desc; +} + +/* + * This function fills the AT FIFO with the (eventual) pending packets + * and runs or wake up the AT DMA prg if necessary. + * The function MUST be called with the d->lock held. + */ +static int dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) +{ + int idx,z; + + if (d->pending_first == NULL || d->free_prgs == 0) + return 0; + + idx = d->prg_ind; + z = (d->pending_first->data_size) ? 3 : 2; + + /* insert the packets into the at dma fifo */ + while (d->free_prgs>0 && d->pending_first) { + insert_packet(ohci, d, d->pending_first); + d->pending_first = d->pending_first->xnext; + } + if (d->pending_first == NULL) + d->pending_last = NULL; + else + PRINT(KERN_INFO, ohci->id, + "AT DMA FIFO ctx=%d full... waiting",d->ctx); + + /* Is the context running ? (should be unless it is + the first packet to be sent in this context) */ + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { + DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx); + reg_write(ohci, d->cmdPtr, d->prg_bus[idx]|z); + run_context(ohci, d->ctrlSet, NULL); } else { - d->first = packet; - d->last = packet; + DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx); + /* wake up the dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) + reg_write(ohci, d->ctrlSet, 0x1000); } + return 1; } +/* + * Transmission of an async packet + */ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) { struct ti_ohci *ohci = host->hostdata; struct dma_trm_ctx *d; unsigned char tcode; - int timeout=50; + unsigned long flags; if (packet->data_size >= ohci->max_packet_size) { PRINT(KERN_ERR, ohci->id, @@ -1305,48 +747,21 @@ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) if (tcode & 0x02) d = ohci->at_resp_context; else d = ohci->at_req_context; - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock,flags); - if (d->free_prgs<1) { - PRINT(KERN_INFO, ohci->id, - "AT DMA ctx=%d Running out of prgs... waiting",d->ctx); - } - while (d->free_prgs<1) { - spin_unlock(&d->lock); - interruptible_sleep_on(&d->waitq); - if(signal_pending(current)) return -EINTR; - if (timeout--<0) { - stop_context(ohci, d->ctrlClear, - "AT DMA runaway loop... bailing out"); - return 0; - } - spin_lock(&d->lock); - } - - insert_packet(ohci, d, packet); - - /* Is the context running ? (should be unless it is - the first packet to be sent in this context) */ - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { - DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx); - if (packet->data_size) - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[d->prg_ind])) | 0x3); - else - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[d->prg_ind])) | 0x2); - - run_context(ohci, d->ctrlSet, NULL); + /* queue the packet for later insertion into to dma fifo */ + if (d->pending_last) { + d->pending_last->xnext = packet; + d->pending_last = packet; } else { - DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx); - /* wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) - reg_write(ohci, d->ctrlSet, 0x1000); + d->pending_first = packet; + d->pending_last = packet; } + + dma_trm_flush(ohci, d); - d->prg_ind = (d->prg_ind+1)%d->num_desc; - spin_unlock(&d->lock); + spin_unlock_irqrestore(&d->lock,flags); return 1; } @@ -1359,7 +774,10 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) switch (cmd) { case RESET_BUS: - host->attempt_root=1; + /* + * FIXME: this flag might be necessary in some case + */ + /* host->attempt_root = 1; */ PRINT(KERN_INFO, ohci->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); @@ -1415,14 +833,9 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) spin_lock_irqsave(&ohci->IR_channel_lock, flags); -#if 0 - PRINT(KERN_INFO, ohci->id, "!!! try listen on channel %d !!!", - arg); -#endif - if (!test_and_set_bit(arg, &ohci->IR_channel_usage)) { - PRINT(KERN_INFO, ohci->id, - "listening enabled on channel %d", arg); + DBGMSG(ohci->id, + "listening enabled on channel %d", arg); if (arg > 31) { u32 setMask= 0x00000001; @@ -1447,8 +860,8 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (test_and_clear_bit(arg, &ohci->IR_channel_usage)) { - PRINT(KERN_INFO, ohci->id, - "listening disabled on iso channel %d", arg); + DBGMSG(ohci->id, + "listening disabled on iso channel %d", arg); if (arg > 31) { u32 clearMask= 0x00000001; @@ -1490,29 +903,42 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) static void dma_trm_reset(struct dma_trm_ctx *d) { struct ti_ohci *ohci; + unsigned long flags; if (d==NULL) { PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg"); return; } ohci = (struct ti_ohci *)(d->ohci); - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock,flags); + + /* is there still any packet pending in the fifo ? */ + while(d->fifo_first) { + PRINT(KERN_INFO, ohci->id, + "AT dma reset ctx=%d, aborting transmission", + d->ctx); + hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED); + d->fifo_first = d->fifo_first->xnext; + } + d->fifo_first = d->fifo_last = NULL; /* is there still any packet pending ? */ - while(d->first) { + while(d->pending_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); - hpsb_packet_sent(ohci->host, d->first, ACKX_ABORTED); - d->first = d->first->xnext; + hpsb_packet_sent(ohci->host, d->pending_first, + ACKX_ABORTED); + d->pending_first = d->pending_first->xnext; } - d->first = d->last = NULL; + d->pending_first = d->pending_last = NULL; + d->branchAddrPtr=NULL; d->sent_ind = d->prg_ind; d->free_prgs = d->num_desc; - spin_unlock(&d->lock); + spin_unlock_irqrestore(&d->lock,flags); } static void ohci_irq_handler(int irq, void *dev_id, @@ -1528,10 +954,10 @@ static void ohci_irq_handler(int irq, void *dev_id, /* read the interrupt event register */ event=reg_read(ohci, OHCI1394_IntEventClear); - DBGMSG(ohci->id, "IntEvent: %08x",event); - if (!event) return; + DBGMSG(ohci->id, "IntEvent: %08x",event); + /* clear the interrupt event register */ reg_write(ohci, OHCI1394_IntEventClear, event); @@ -1560,7 +986,7 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got reqTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "reqTxComplete"); else dma_trm_bh((void *)d); @@ -1570,7 +996,7 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got respTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "respTxComplete"); else dma_trm_bh((void *)d); @@ -1580,9 +1006,9 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RQPkt"); + ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1595,9 +1021,9 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RSPkt"); + ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1608,9 +1034,6 @@ static void ohci_irq_handler(int irq, void *dev_id, if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; struct dma_rcv_ctx *d = ohci->ir_context; -#ifdef _VIDEO_1394_H - int i; -#endif isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, @@ -1620,10 +1043,10 @@ static void ohci_irq_handler(int irq, void *dev_id, reg_read(ohci, d->ctrlSet), isoRecvIntEvent); if (isoRecvIntEvent & 0x1) { if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "isochRx"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1631,12 +1054,21 @@ static void ohci_irq_handler(int irq, void *dev_id, #endif } } -#ifdef _VIDEO_1394_H - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (isoRecvIntEvent & (1<<(i+1))) - wakeup_dma_fbuf_ctx( - ohci,ohci->fbuf_context[i]); -#endif + if (ohci->video_tmpl) + ohci->video_tmpl->irq_handler(ohci->id, + isoRecvIntEvent, + 0); + } + if (event & OHCI1394_isochTx) { + quadlet_t isoXmitIntEvent; + isoXmitIntEvent = + reg_read(ohci, OHCI1394_IsoXmitIntEventSet); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, + isoXmitIntEvent); + DBGMSG(ohci->id, "Got isochTx interrupt"); + if (ohci->video_tmpl) + ohci->video_tmpl->irq_handler(ohci->id, 0, + isoXmitIntEvent); } if (event & OHCI1394_selfIDComplete) { if (host->in_bus_reset) { @@ -1703,10 +1135,10 @@ static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); DBGMSG(ohci->id, "Inserting dma buf ctx=%d idx=%d", d->ctx, idx); - d->prg[idx]->status = d->buf_size; - d->prg[idx]->branchAddress &= 0xfffffff0; + d->prg_cpu[idx]->status = d->buf_size; + d->prg_cpu[idx]->branchAddress &= 0xfffffff0; idx = (idx + d->num_desc - 1 ) % d->num_desc; - d->prg[idx]->branchAddress |= 0x1; + d->prg_cpu[idx]->branchAddress |= 0x1; /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { @@ -1724,7 +1156,7 @@ static int block_length(struct dma_rcv_ctx *d, int idx, /* Where is the data length ? */ if (offset+12>=d->buf_size) - length = (d->buf[(idx+1)%d->num_desc] + length = (d->buf_cpu[(idx+1)%d->num_desc] [3-(d->buf_size-offset)/4]>>16); else length = (buf_ptr[3]>>16); @@ -1769,7 +1201,7 @@ static void dma_rcv_bh(void *data) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); unsigned int split_left, idx, offset, rescount; unsigned char tcode; - int length, bytes_left; + int length, bytes_left, ack; quadlet_t *buf_ptr; char *split_ptr; char msg[256]; @@ -1778,9 +1210,9 @@ static void dma_rcv_bh(void *data) idx = d->buf_ind; offset = d->buf_offset; - buf_ptr = d->buf[idx] + offset/4; + buf_ptr = d->buf_cpu[idx] + offset/4; - rescount = d->prg[idx]->status&0xffff; + rescount = d->prg_cpu[idx]->status&0xffff; bytes_left = d->buf_size - rescount - offset; while (bytes_left>0) { @@ -1790,21 +1222,22 @@ static void dma_rcv_bh(void *data) if (length<4) { /* something is wrong */ sprintf(msg,"unexpected tcode 0x%X in AR ctx=%d", tcode, d->ctx); - stop_context(ohci, d->ctrlClear, msg); + ohci1394_stop_context(ohci, d->ctrlClear, msg); spin_unlock(&d->lock); return; } if ((offset+length)>d->buf_size) { /* Split packet */ if (length>d->split_buf_size) { - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "split packet size exceeded"); d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); return; } - if (d->prg[(idx+1)%d->num_desc]->status==d->buf_size) { + if (d->prg_cpu[(idx+1)%d->num_desc]->status + ==d->buf_size) { /* other part of packet not written yet */ /* this should never happen I think */ /* anyway we'll get it on the next call */ @@ -1822,7 +1255,7 @@ static void dma_rcv_bh(void *data) split_ptr += d->buf_size-offset; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; offset=0; while (split_left >= d->buf_size) { memcpy(split_ptr,buf_ptr,d->buf_size); @@ -1830,7 +1263,7 @@ static void dma_rcv_bh(void *data) split_left -= d->buf_size; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; } if (split_left>0) { memcpy(split_ptr, buf_ptr, split_left); @@ -1838,17 +1271,6 @@ static void dma_rcv_bh(void *data) buf_ptr += offset/4; } - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * We need to handle write requests that are received - * to our middle address space (posted writes). - * In this case, the hardware generates an - * ack_complete... but, if we pass the packet up to - * the subsystem, it will try and send a response - * (which it shouldn't), because it assumes we - * returned ack_pending. - */ - /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may @@ -1862,22 +1284,12 @@ static void dma_rcv_bh(void *data) (d->spb[length/4-1]>>16)&0x1f, (d->spb[length/4-1]>>21)&0x3, tcode, length, d->spb[3], d->ctx); + + ack = (((d->spb[length/4-1]>>16)&0x1f) + == 0x11) ? 1 : 0; - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * Handle case of posted writes. If we receive - * an ack_complete, we should not send a - * response. Fake out upper layers by turning - * the packet into a broadcast packet... we - * should really modify the core stack to - * accept an ack received argument and figure - * out whether to reply. - */ - if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { - d->spb[0] |= (ALL_NODES<<16); - } hpsb_packet_received(ohci->host, d->spb, - length); + length, ack); } else PRINT(KERN_INFO, ohci->id, @@ -1899,21 +1311,11 @@ static void dma_rcv_bh(void *data) (buf_ptr[length/4-1]>>21)&0x3, tcode, length, buf_ptr[3], d->ctx); - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * Handle case of posted writes. If we receive - * an ack_complete, we should not send a - * response. Fake out upper layers by turning - * the packet into a broadcast packet... we - * should really modify the core stack to - * accept an ack received argument and figure - * out whether to reply. - */ - if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { - buf_ptr[0] |= (ALL_NODES<<16); - } + ack = (((buf_ptr[length/4-1]>>16)&0x1f) + == 0x11) ? 1 : 0; + hpsb_packet_received(ohci->host, buf_ptr, - length); + length, ack); } else PRINT(KERN_INFO, ohci->id, @@ -1924,11 +1326,11 @@ static void dma_rcv_bh(void *data) if (offset==d->buf_size) { insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; offset=0; } } - rescount = d->prg[idx]->status & 0xffff; + rescount = d->prg_cpu[idx]->status & 0xffff; bytes_left = d->buf_size - rescount - offset; } @@ -1945,32 +1347,51 @@ static void dma_trm_bh(void *data) struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); struct hpsb_packet *packet; + unsigned long flags; u32 ack; - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock, flags); - if (d->first==NULL) { - stop_context(ohci, d->ctrlClear, + if (d->fifo_first==NULL) { +#if 0 + ohci1394_stop_context(ohci, d->ctrlClear, "Packet sent ack received but queue is empty"); - spin_unlock(&d->lock); +#endif + spin_unlock_irqrestore(&d->lock, flags); return; } - packet = d->first; - d->first = d->first->xnext; - if (d->first==NULL) d->last=NULL; - if (packet->data_size) - ack = d->prg[d->sent_ind].end.status>>16; - else - ack = d->prg[d->sent_ind].begin.status>>16; - d->sent_ind = (d->sent_ind+1)%d->num_desc; - d->free_prgs++; - spin_unlock(&d->lock); - if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + while (d->fifo_first) { + packet = d->fifo_first; + if (packet->data_size) + ack = d->prg_cpu[d->sent_ind]->end.status>>16; + else + ack = d->prg_cpu[d->sent_ind]->begin.status>>16; + + if (ack==0) + /* this packet hasn't been sent yet*/ + break; + + DBGMSG(ohci->id, + "Packet sent to node %d ack=0x%X spd=%d ctx=%d", + (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, + d->ctx); + hpsb_packet_sent(ohci->host, packet, ack&0xf); + + if (packet->data_size) + pci_unmap_single(ohci->dev, + d->prg_cpu[d->sent_ind]->end.address, + packet->data_size, PCI_DMA_TODEVICE); + + d->sent_ind = (d->sent_ind+1)%d->num_desc; + d->free_prgs++; + d->fifo_first = d->fifo_first->xnext; + } + if (d->fifo_first==NULL) d->fifo_last=NULL; + + dma_trm_flush(ohci, d); - DBGMSG(ohci->id, "Packet sent to node %d ack=0x%X spd=%d ctx=%d", - (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, d->ctx); - hpsb_packet_sent(ohci->host, packet, ack&0xf); + spin_unlock_irqrestore(&d->lock, flags); } static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) @@ -1984,17 +1405,25 @@ static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx); - stop_context(ohci, (*d)->ctrlClear, NULL); + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); - if ((*d)->buf) { - for (i=0; i<(*d)->num_desc; i++) - if ((*d)->buf[i]) kfree((*d)->buf[i]); - kfree((*d)->buf); - } - if ((*d)->prg) { + if ((*d)->buf_cpu) { for (i=0; i<(*d)->num_desc; i++) - if ((*d)->prg[i]) kfree((*d)->prg[i]); - kfree((*d)->prg); + if ((*d)->buf_cpu[i] && (*d)->buf_bus[i]) + pci_free_consistent( + ohci->dev, (*d)->buf_size, + (*d)->buf_cpu[i], (*d)->buf_bus[i]); + kfree((*d)->buf_cpu); + kfree((*d)->buf_bus); + } + if ((*d)->prg_cpu) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) + pci_free_consistent( + ohci->dev, sizeof(struct dma_cmd), + (*d)->prg_cpu[i], (*d)->prg_bus[i]); + kfree((*d)->prg_cpu); + kfree((*d)->prg_bus); } if ((*d)->spb) kfree((*d)->spb); @@ -2030,27 +1459,34 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; - d->buf = NULL; - d->prg = NULL; + d->buf_cpu = NULL; + d->buf_bus = NULL; + d->prg_cpu = NULL; + d->prg_bus = NULL; d->spb = NULL; - d->buf = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); + d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); + d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->buf == NULL) { + if (d->buf_cpu == NULL || d->buf_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); free_dma_rcv_ctx(&d); return NULL; } - memset(d->buf, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); - d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd*), GFP_KERNEL); + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->prg == NULL) { + if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); free_dma_rcv_ctx(&d); return NULL; } - memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); d->spb = kmalloc(d->split_buf_size, GFP_KERNEL); @@ -2061,10 +1497,12 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, } for (i=0; i<d->num_desc; i++) { - d->buf[i] = kmalloc(d->buf_size, GFP_KERNEL); + d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, + d->buf_size, + d->buf_bus+i); - if (d->buf[i] != NULL) { - memset(d->buf[i], 0, d->buf_size); + if (d->buf_cpu[i] != NULL) { + memset(d->buf_cpu[i], 0, d->buf_size); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); @@ -2072,10 +1510,13 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, return NULL; } - d->prg[i]= kmalloc(sizeof(struct dma_cmd), GFP_KERNEL); + + d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, + sizeof(struct dma_cmd), + d->prg_bus+i); - if (d->prg[i] != NULL) { - memset(d->prg[i], 0, sizeof(struct dma_cmd)); + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); @@ -2098,6 +1539,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, static int free_dma_trm_ctx(struct dma_trm_ctx **d) { struct ti_ohci *ohci; + int i; if (*d==NULL) return -1; @@ -2105,9 +1547,18 @@ static int free_dma_trm_ctx(struct dma_trm_ctx **d) DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx); - stop_context(ohci, (*d)->ctrlClear, NULL); + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->prg_cpu) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) + pci_free_consistent( + ohci->dev, sizeof(struct at_dma_prg), + (*d)->prg_cpu[i], (*d)->prg_bus[i]); + kfree((*d)->prg_cpu); + kfree((*d)->prg_bus); + } - if ((*d)->prg) kfree((*d)->prg); kfree(*d); *d = NULL; return 0; @@ -2118,6 +1569,7 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, int ctrlSet, int ctrlClear, int cmdPtr) { struct dma_trm_ctx *d=NULL; + int i; d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL); @@ -2133,16 +1585,35 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->ctrlSet = ctrlSet; d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; - d->prg = NULL; + d->prg_cpu = NULL; + d->prg_bus = NULL; - d->prg = kmalloc(d->num_desc * sizeof(struct at_dma_prg), GFP_KERNEL); + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->prg == NULL) { + if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg"); free_dma_trm_ctx(&d); return NULL; } - memset(d->prg, 0, d->num_desc * sizeof(struct at_dma_prg)); + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + for (i=0; i<d->num_desc; i++) { + d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, + sizeof(struct at_dma_prg), + d->prg_bus+i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); + } else { + PRINT(KERN_ERR, ohci->id, + "failed to allocate at dma prg"); + free_dma_trm_ctx(&d); + return NULL; + } + } spin_lock_init(&d->lock); @@ -2150,15 +1621,42 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->task.routine = dma_trm_bh; d->task.data = (void*)d; - init_waitqueue_head(&d->waitq); - return d; } +static u32 ohci_crc16(unsigned *data, int length) +{ + int check=0, i; + int shift, sum, next=0; + + for (i = length; i; i--) { + for (next = check, shift = 28; shift >= 0; shift -= 4 ) { + sum = ((next >> 12) ^ (*data >> shift)) & 0xf; + next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + check = next & 0xffff; + data++; + } + + return check; +} + +static void ohci_init_config_rom(struct ti_ohci *ohci) +{ + int i; + + ohci_csr_rom[3] = reg_read(ohci, OHCI1394_GUIDHi); + ohci_csr_rom[4] = reg_read(ohci, OHCI1394_GUIDLo); + + ohci_csr_rom[0] = 0x04040000 | ohci_crc16(ohci_csr_rom+1, 4); + + for (i=0;i<sizeof(ohci_csr_rom)/4;i++) + ohci->csr_config_rom_cpu[i] = cpu_to_be32(ohci_csr_rom[i]); +} + static int add_card(struct pci_dev *dev) { struct ti_ohci *ohci; /* shortcut to currently handled device */ - int i; if (num_of_cards == MAX_OHCI1394_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " @@ -2182,21 +1680,29 @@ static int add_card(struct pci_dev *dev) ohci->state = 0; /* csr_config rom allocation */ - ohci->csr_config_rom = kmalloc(1024, GFP_KERNEL); - if (ohci->csr_config_rom == NULL) { + ohci->csr_config_rom_cpu = + pci_alloc_consistent(ohci->dev, sizeof(ohci_csr_rom), + &ohci->csr_config_rom_bus); + if (ohci->csr_config_rom_cpu == NULL) { FAIL("failed to allocate buffer config rom"); } - for (i=0;i<sizeof(ohci_csr_rom)/4;i++) - ohci->csr_config_rom[i] = cpu_to_be32(ohci_csr_rom[i]); DBGMSG(ohci->id, "The 1st byte at offset 0x404 is: 0x%02x", - *((char *)ohci->csr_config_rom+4)); + *((char *)ohci->csr_config_rom_cpu+4)); - /* self-id dma buffer allocation */ - ohci->self_id_buffer = kmalloc(2048, GFP_KERNEL); - if (ohci->self_id_buffer == NULL) { + /* + * self-id dma buffer allocation + * FIXME: some early chips may need 8KB alignment for the + * selfid buffer + */ + ohci->selfid_buf_cpu = + pci_alloc_consistent(ohci->dev, 2048, &ohci->selfid_buf_bus); + if (ohci->selfid_buf_cpu == NULL) { FAIL("failed to allocate DMA buffer for self-id packets"); } + if ((unsigned long)ohci->selfid_buf_cpu & 0xfff) + PRINT(KERN_INFO, ohci->id, "Selfid buffer %p not aligned on " + "8Kb boundary", ohci->selfid_buf_cpu); ohci->ar_req_context = alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC, @@ -2241,9 +1747,9 @@ static int add_card(struct pci_dev *dev) ohci->ir_context = alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC, IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, - OHCI1394_IrRcvContextControlSet, - OHCI1394_IrRcvContextControlClear, - OHCI1394_IrRcvCommandPtr); + OHCI1394_IsoRcvContextControlSet, + OHCI1394_IsoRcvContextControlClear, + OHCI1394_IsoRcvCommandPtr); if (ohci->ir_context == NULL) { FAIL("failed to allocate IR context"); @@ -2274,6 +1780,8 @@ static int add_card(struct pci_dev *dev) FAIL("failed to allocate shared interrupt %d", dev->irq); } + ohci_init_config_rom(ohci); + return 0; #undef FAIL } @@ -2297,12 +1805,9 @@ int ohci_get_info(char *buf, char **start, off_t fpos, //unsigned char phyreg; //int i, nports; int i; + struct dma_rcv_ctx *d=NULL; struct dma_trm_ctx *dt=NULL; -#ifdef _VIDEO_1394_H - int j; - struct dma_fbuf_ctx *f=ohci->fbuf_context[0]; -#endif p += sprintf(p,"IEEE-1394 OHCI Driver status report:\n"); p += sprintf(p," bus number: 0x%x Node ID: 0x%x\n", @@ -2332,26 +1837,6 @@ int ohci_get_info(char *buf, char **start, off_t fpos, p += sprintf(p,"\n---Iso Receive DMA---\n"); -#ifdef _VIDEO_1394_H - -#if 0 - if (f!=NULL) { - for (i=0; i<f->num_desc; i++) { - for (j=0;j<f->nb_cmd;j++) { - p += sprintf(p, - "prg[%d][%d]: %p %08x %08x %08x %08x\n", - i,j,virt_to_bus(&(f->prg[i][j])), - f->prg[i][j].control, - f->prg[i][j].address, - f->prg[i][j].branchAddress, - f->prg[i][j].status); - } - } - } -#endif - -#else - d = ohci->ir_context; #if 0 for (i=0; i<d->num_desc; i++) { @@ -2389,7 +1874,7 @@ int ohci_get_info(char *buf, char **start, off_t fpos, dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT req queue: first: %p last: %p\n", - dt->first, dt->last); + dt->fifo_first, dt->fifo_last); dt = ohci->at_resp_context; #if 0 for (i=0; i<dt->num_desc; i++) { @@ -2437,15 +1922,14 @@ int ohci_get_info(char *buf, char **start, off_t fpos, dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT resp queue: first: %p last: %p\n", - dt->first, dt->last); -#endif + dt->fifo_first, dt->fifo_last); /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", OHCI1394_Version, OHCI1394_GUID_ROM, OHCI1394_ATRetries); - SR("CSRReadData : %08x CSRCompData : %08x CSRControl : %08x\n", - OHCI1394_CSRReadData, OHCI1394_CSRCompareData, OHCI1394_CSRControl); + SR("CSRData : %08x CSRCompData : %08x CSRControl : %08x\n", + OHCI1394_CSRData, OHCI1394_CSRCompareData, OHCI1394_CSRControl); SR("ConfigROMhdr: %08x BusID : %08x BusOptions : %08x\n", OHCI1394_ConfigROMhdr, OHCI1394_BusID, OHCI1394_BusOptions); SR("GUIDHi : %08x GUIDLo : %08x ConfigROMmap: %08x\n", @@ -2481,14 +1965,21 @@ int ohci_get_info(char *buf, char **start, off_t fpos, SR("AsRsRvCtxCtl: %08x AsRsRvCmdPtr: %08x IntEvent : %08x\n", OHCI1394_AsRspRcvContextControlSet, OHCI1394_AsRspRcvCommandPtr, OHCI1394_IntEventSet); - for (i=0;i<4;i++) { + for (i=0;i<ohci->nb_iso_rcv_ctx;i++) { p += sprintf(p,"IsoRCtxCtl%02d: %08x IsoRCmdPtr%02d: %08x" " IsoRCxtMch%02d: %08x\n", i, reg_read(ohci, - OHCI1394_IrRcvContextControlSet+32*i), - i,reg_read(ohci, OHCI1394_IrRcvCommandPtr+32*i), + OHCI1394_IsoRcvContextControlSet+32*i), + i,reg_read(ohci, OHCI1394_IsoRcvCommandPtr+32*i), i,reg_read(ohci, - OHCI1394_IrRcvContextMatch+32*i)); + OHCI1394_IsoRcvContextMatch+32*i)); + } + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) { + p += sprintf(p,"IsoTCtxCtl%02d: %08x IsoTCmdPtr%02d: %08x\n", + i, + reg_read(ohci, + OHCI1394_IsoXmitContextControlSet+32*i), + i,reg_read(ohci,OHCI1394_IsoXmitCommandPtr+32*i)); } #if 0 @@ -2554,9 +2045,11 @@ struct proc_dir_entry ohci_proc_entry = static void remove_card(struct ti_ohci *ohci) { -#ifdef _VIDEO_1394_H - int i; -#endif + /* + * Reset the board properly before leaving + * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> + */ + ohci_soft_reset(ohci); /* Free AR dma */ free_dma_rcv_ctx(&ohci->ar_req_context); @@ -2569,28 +2062,18 @@ static void remove_card(struct ti_ohci *ohci) /* Free IR dma */ free_dma_rcv_ctx(&ohci->ir_context); -#ifdef _VIDEO_1394_H - /* Free the frame buffer context */ - if (ohci->fbuf_context) - for (i=0;i<ohci->nb_iso_ctx-1;i++) { - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - } -#endif - - /* - * Reset the board properly before leaving - * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> - */ - ohci_soft_reset(ohci); - /* Free self-id buffer */ - if (ohci->self_id_buffer) - kfree(ohci->self_id_buffer); + if (ohci->selfid_buf_cpu) + pci_free_consistent(ohci->dev, 2048, + ohci->selfid_buf_cpu, + ohci->selfid_buf_bus); /* Free config rom */ - if (ohci->csr_config_rom) - kfree(ohci->csr_config_rom); - + if (ohci->csr_config_rom_cpu) + pci_free_consistent(ohci->dev, sizeof(ohci_csr_rom), + ohci->csr_config_rom_cpu, + ohci->csr_config_rom_bus); + /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); @@ -2646,9 +2129,9 @@ static size_t get_ohci_rom(struct hpsb_host *host, const quadlet_t **ptr) struct ti_ohci *ohci=host->hostdata; DBGMSG(ohci->id, "request csr_rom address: %08X", - (u32)ohci->csr_config_rom); + (u32)ohci->csr_config_rom_cpu); - *ptr = ohci->csr_config_rom; + *ptr = ohci->csr_config_rom_cpu; return sizeof(ohci_csr_rom); } @@ -2675,6 +2158,118 @@ struct hpsb_host_template *get_ohci_template(void) return &tmpl; } +void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) +{ + int i=0; + + /* stop the channel program if it's still running */ + reg_write(ohci, reg, 0x8000); + + /* Wait until it effectively stops */ + while (reg_read(ohci, reg) & 0x400) { + i++; + if (i>5000) { + PRINT(KERN_ERR, ohci->id, + "runaway loop while stopping context..."); + break; + } + } + if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg); +} + +struct ti_ohci *ohci1394_get_struct(int card_num) +{ + if (card_num>=0 && card_num<num_of_cards) + return &cards[card_num]; + return NULL; +} + +int ohci1394_register_video(struct ti_ohci *ohci, + struct video_template *tmpl) +{ + if (ohci->video_tmpl) + return -ENFILE; + ohci->video_tmpl = tmpl; + MOD_INC_USE_COUNT; + return 0; +} + +void ohci1394_unregister_video(struct ti_ohci *ohci, + struct video_template *tmpl) +{ + if (ohci->video_tmpl != tmpl) { + PRINT(KERN_ERR, ohci->id, + "Trying to unregister wrong video device"); + } + else { + ohci->video_tmpl = NULL; + MOD_DEC_USE_COUNT; + } +} + +#if 0 +int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data, + quadlet_t compare, int sel) +{ + int timeout = 255; + reg_write(ohci, OHCI1394_CSRData, *data); + reg_write(ohci, OHCI1394_CSRCompareData, compare); + reg_write(ohci, OHCI1394_CSRControl, sel); + while(!(reg_read(ohci, OHCI1394_CSRControl)&0x80000000)) { + if (timeout--) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + } + *data = reg_read(ohci, OHCI1394_CSRData); + return 0; +} + +int ohci1394_request_channel(struct ti_ohci *ohci, int channel) +{ + int csrSel; + quadlet_t chan, data1=0, data2=0; + int timeout = 32; + + if (channel<32) { + chan = 1<<channel; + csrSel = 2; + } + else { + chan = 1<<(channel-32); + csrSel = 3; + } + if (ohci_compare_swap(ohci, &data1, 0, csrSel)<0) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + while (timeout--) { + if (data1 & chan) { + PRINT(KERN_INFO, ohci->id, + "request channel %d failed", channel); + return -1; + } + data2 = data1; + data1 |= chan; + if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + if (data1==data2) { + PRINT(KERN_INFO, ohci->id, + "request channel %d succeded", channel); + return 0; + } + } + PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel); + return -1; +} +#endif + +EXPORT_SYMBOL(ohci1394_stop_context); +EXPORT_SYMBOL(ohci1394_get_struct); +EXPORT_SYMBOL(ohci1394_register_video); +EXPORT_SYMBOL(ohci1394_unregister_video); #ifdef MODULE @@ -2695,10 +2290,6 @@ void cleanup_module(void) #endif #endif -#ifdef _VIDEO_1394_H - unregister_chrdev(OHCI1394_MAJOR, "ohci1394"); -#endif - PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } @@ -2709,17 +2300,8 @@ int init_module(void) if (hpsb_register_lowlevel(get_ohci_template())) { PRINT_G(KERN_ERR, "registering failed\n"); return -ENXIO; - } else { -#ifdef _VIDEO_1394_H - if (register_chrdev(OHCI1394_MAJOR, "ohci1394", &ohci_fops)) - { - printk("ohci1394: unable to get major %d\n", - OHCI1394_MAJOR); - return -EIO; - } -#endif - return 0; - } + } + return 0; } #endif /* MODULE */ diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index d778cbe7d..af1aeb2a6 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -22,8 +22,8 @@ #define _OHCI1394_H #include "ieee1394_types.h" -/* include this for the video frame grabber */ -/* #include "video1394.h" */ + +#define IEEE1394_USE_BOTTOM_HALVES 0 #define OHCI1394_DRIVER_NAME "ohci1394" @@ -71,6 +71,18 @@ #define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 #endif +#ifndef PCI_DEVICE_ID_ALI_OHCI1394_M5251 +#define PCI_DEVICE_ID_ALI_OHCI1394_M5251 0x5251 +#endif + +#ifndef PCI_VENDOR_ID_LUCENT +#define PCI_VENDOR_ID_LUCENT 0x11c1 +#endif + +#ifndef PCI_DEVICE_ID_LUCENT_FW323 +#define PCI_DEVICE_ID_LUCENT_FW323 0x5811 +#endif + #define MAX_OHCI1394_CARDS 4 #define OHCI1394_MAX_AT_REQ_RETRIES 0x2 @@ -87,8 +99,8 @@ #define AR_RESP_SPLIT_BUF_SIZE 4096 /* split packet buffer */ #define IR_NUM_DESC 16 /* number of IR descriptors */ -#define IR_BUF_SIZE 6480 /* 6480 bytes/buffer */ -#define IR_SPLIT_BUF_SIZE 8192 /* split packet buffer */ +#define IR_BUF_SIZE 4096 /* 6480 bytes/buffer */ +#define IR_SPLIT_BUF_SIZE 4096 /* split packet buffer */ #define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */ #define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */ @@ -113,8 +125,15 @@ struct dma_rcv_ctx { unsigned int num_desc; unsigned int buf_size; unsigned int split_buf_size; - struct dma_cmd **prg; - quadlet_t **buf; + + /* dma block descriptors */ + struct dma_cmd **prg_cpu; + dma_addr_t *prg_bus; + + /* dma buffers */ + quadlet_t **buf_cpu; + dma_addr_t *buf_bus; + unsigned int buf_ind; unsigned int buf_offset; quadlet_t *spb; @@ -130,45 +149,37 @@ struct dma_trm_ctx { void *ohci; int ctx; unsigned int num_desc; - struct at_dma_prg *prg; + + /* dma block descriptors */ + struct at_dma_prg **prg_cpu; + dma_addr_t *prg_bus; + unsigned int prg_ind; unsigned int sent_ind; int free_prgs; quadlet_t *branchAddrPtr; - struct hpsb_packet *first; - struct hpsb_packet *last; + + /* list of packets inserted in the AT FIFO */ + struct hpsb_packet *fifo_first; + struct hpsb_packet *fifo_last; + + /* list of pending packets to be inserted in the AT FIFO */ + struct hpsb_packet *pending_first; + struct hpsb_packet *pending_last; + spinlock_t lock; struct tq_struct task; int ctrlClear; int ctrlSet; int cmdPtr; - wait_queue_head_t waitq; }; -#ifdef _VIDEO_1394_H - -#define OHCI1394_MAJOR 172 -#define ISO_CHANNELS 64 - -struct dma_fbuf_ctx { - void *ohci; - int ctx; - int channel; - int last_buffer; - unsigned int num_desc; - unsigned int buf_size; - unsigned int frame_size; - unsigned int nb_cmd; - unsigned char *buf; - struct dma_cmd **prg; - unsigned int *buffer_status; - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxMatch; - wait_queue_head_t waitq; +/* video device template */ +struct video_template { + void (*irq_handler) (int card, quadlet_t isoRecvEvent, + quadlet_t isoXmitEvent); }; -#endif + struct ti_ohci { int id; /* sequential card number */ @@ -180,8 +191,13 @@ struct ti_ohci { /* remapped memory spaces */ void *registers; - quadlet_t *self_id_buffer; /* dma buffer for self-id packets */ - quadlet_t *csr_config_rom; /* buffer for csr config rom */ + /* dma buffer for self-id packets */ + quadlet_t *selfid_buf_cpu; + dma_addr_t selfid_buf_bus; + + /* buffer for csr config rom */ + quadlet_t *csr_config_rom_cpu; + dma_addr_t csr_config_rom_bus; unsigned int max_packet_size; @@ -197,13 +213,10 @@ struct ti_ohci { struct dma_rcv_ctx *ir_context; u64 IR_channel_usage; spinlock_t IR_channel_lock; - int nb_iso_ctx; + int nb_iso_rcv_ctx; -#ifdef _VIDEO_1394_H - /* frame buffer context */ - struct dma_fbuf_ctx **fbuf_context; - struct dma_fbuf_ctx *current_fbuf_ctx; -#endif + /* iso transmit */ + int nb_iso_xmit_ctx; /* IEEE-1394 part follows */ struct hpsb_host *host; @@ -214,8 +227,23 @@ struct ti_ohci { int self_id_errors; int NumBusResets; + + /* video device */ + struct video_template *video_tmpl; }; +inline static int cross_bound(unsigned long addr, unsigned int size) +{ + int cross=0; + if (size>PAGE_SIZE) { + cross = size/PAGE_SIZE; + size -= cross*PAGE_SIZE; + } + if ((PAGE_SIZE-addr%PAGE_SIZE)<size) + cross++; + return cross; +} + /* * Register read and write helper functions. */ @@ -310,7 +338,7 @@ quadlet_t ohci_csr_rom[] = { #define OHCI1394_Version 0x000 #define OHCI1394_GUID_ROM 0x004 #define OHCI1394_ATRetries 0x008 -#define OHCI1394_CSRReadData 0x00C +#define OHCI1394_CSRData 0x00C #define OHCI1394_CSRCompareData 0x010 #define OHCI1394_CSRControl 0x014 #define OHCI1394_ConfigROMhdr 0x018 @@ -370,12 +398,18 @@ quadlet_t ohci_csr_rom[] = { #define OHCI1394_AsRspRcvContextControlClear 0x1E4 #define OHCI1394_AsRspRcvCommandPtr 0x1EC +/* Isochronous transmit registers */ +/* Add (32 * n) for context n */ +#define OHCI1394_IsoXmitContextControlSet 0x200 +#define OHCI1394_IsoXmitContextControlClear 0x204 +#define OHCI1394_IsoXmitCommandPtr 0x20C + /* Isochronous receive registers */ /* Add (32 * n) for context n */ -#define OHCI1394_IrRcvContextControlSet 0x400 -#define OHCI1394_IrRcvContextControlClear 0x404 -#define OHCI1394_IrRcvCommandPtr 0x40C -#define OHCI1394_IrRcvContextMatch 0x410 +#define OHCI1394_IsoRcvContextControlSet 0x400 +#define OHCI1394_IsoRcvContextControlClear 0x404 +#define OHCI1394_IsoRcvCommandPtr 0x40C +#define OHCI1394_IsoRcvContextMatch 0x410 /* Interrupts Mask/Events */ @@ -410,5 +444,12 @@ quadlet_t ohci_csr_rom[] = { #define DMA_SPEED_200 0x1 #define DMA_SPEED_400 0x2 +void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); +struct ti_ohci *ohci1394_get_struct(int card_num); +int ohci1394_register_video(struct ti_ohci *ohci, + struct video_template *tmpl); +void ohci1394_unregister_video(struct ti_ohci *ohci, + struct video_template *tmpl); + #endif diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c index 368c5137b..16aeaa92d 100644 --- a/drivers/ieee1394/pcilynx.c +++ b/drivers/ieee1394/pcilynx.c @@ -82,7 +82,7 @@ static pcl_t alloc_pcl(struct ti_lynx *lynx) spin_lock(&lynx->lock); /* FIXME - use ffz() to make this readable */ - for (i = 0; i < LOCALRAM_SIZE; i++) { + for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { m = lynx->pcl_bmap[i]; for (j = 0; j < 8; j++) { if (m & 1<<j) { @@ -385,11 +385,22 @@ static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host, size_t s -/* This must be called with the async_queue_lock held. */ +/* This must be called with the async.queue_lock held. */ static void send_next_async(struct ti_lynx *lynx) { struct ti_pcl pcl; - struct hpsb_packet *packet = lynx->async_queue; + struct hpsb_packet *packet = lynx->async.queue; + + lynx->async.header_dma = pci_map_single(lynx->dev, packet->header, + packet->header_size, + PCI_DMA_TODEVICE); + if (packet->data_size) { + lynx->async.data_dma = pci_map_single(lynx->dev, packet->data, + packet->data_size, + PCI_DMA_TODEVICE); + } else { + lynx->async.data_dma = 0; + } pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID; @@ -400,16 +411,16 @@ static void send_next_async(struct ti_lynx *lynx) pcl.buffer[0].control = PCL_CMD_XMT | packet->speed_code << 14 | packet->header_size | PCL_BIGENDIAN; #endif - pcl.buffer[0].pointer = virt_to_bus(packet->header); + pcl.buffer[0].pointer = lynx->async.header_dma; pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; - pcl.buffer[1].pointer = virt_to_bus(packet->data); + pcl.buffer[1].pointer = lynx->async.data_dma; if (!packet->data_be) { pcl.buffer[1].control |= PCL_BIGENDIAN; } - put_pcl(lynx, lynx->async_pcl, &pcl); - run_pcl(lynx, lynx->async_pcl_start, 3); + put_pcl(lynx, lynx->async.pcl, &pcl); + run_pcl(lynx, lynx->async.pcl_start, 3); } @@ -440,8 +451,8 @@ static int lynx_initialize(struct hpsb_host *host) int i; u32 *pcli; - lynx->async_queue = NULL; - spin_lock_init(&lynx->async_queue_lock); + lynx->async.queue = NULL; + spin_lock_init(&lynx->async.queue_lock); spin_lock_init(&lynx->phy_reg_lock); pcl.next = pcl_bus(lynx, lynx->rcv_pcl); @@ -456,13 +467,13 @@ static int lynx_initialize(struct hpsb_host *host) pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16; pcl.buffer[1].control = PCL_LAST_BUFF | 4080; #endif - pcl.buffer[0].pointer = virt_to_bus(lynx->rcv_page); - pcl.buffer[1].pointer = virt_to_bus(lynx->rcv_page) + 16; + pcl.buffer[0].pointer = lynx->rcv_page_dma; + pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; put_pcl(lynx, lynx->rcv_pcl, &pcl); - pcl.next = pcl_bus(lynx, lynx->async_pcl); - pcl.async_error_next = pcl_bus(lynx, lynx->async_pcl); - put_pcl(lynx, lynx->async_pcl_start, &pcl); + pcl.next = pcl_bus(lynx, lynx->async.pcl); + pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); + put_pcl(lynx, lynx->async.pcl_start, &pcl); pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID; @@ -476,7 +487,7 @@ static int lynx_initialize(struct hpsb_host *host) int page = i / ISORCV_PER_PAGE; int sec = i % ISORCV_PER_PAGE; - pcl.buffer[0].pointer = virt_to_bus(lynx->iso_rcv.page[page]) + pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] + sec * MAX_ISORCV_SIZE; pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); @@ -542,7 +553,9 @@ static void lynx_release(struct hpsb_host *host) lynx = host->hostdata; remove_card(lynx); } else { +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif } } @@ -568,13 +581,13 @@ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) cpu_to_be32s(&packet->header[3]); } - spin_lock_irqsave(&lynx->async_queue_lock, flags); + spin_lock_irqsave(&lynx->async.queue_lock, flags); - if (lynx->async_queue == NULL) { - lynx->async_queue = packet; + if (lynx->async.queue == NULL) { + lynx->async.queue = packet; send_next_async(lynx); } else { - p = lynx->async_queue; + p = lynx->async.queue; while (p->xnext != NULL) { p = p->xnext; } @@ -582,7 +595,7 @@ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) p->xnext = packet; } - spin_unlock_irqrestore(&lynx->async_queue_lock, flags); + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); return 1; } @@ -637,13 +650,13 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) break; case CANCEL_REQUESTS: - spin_lock_irqsave(&lynx->async_queue_lock, flags); + spin_lock_irqsave(&lynx->async.queue_lock, flags); reg_write(lynx, DMA3_CHAN_CTRL, 0); - packet = lynx->async_queue; - lynx->async_queue = NULL; + packet = lynx->async.queue; + lynx->async.queue = NULL; - spin_unlock_irqrestore(&lynx->async_queue_lock, flags); + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); while (packet != NULL) { lastpacket = packet; @@ -696,7 +709,7 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) * IEEE-1394 functionality section END * ***************************************/ - +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS /* VFS functions for local bus / aux device access. Access to those * is implemented as a character device instead of block devices * because buffers are not wanted for this. Therefore llseek (from @@ -710,7 +723,7 @@ static ssize_t mem_write(struct file*, const char*, size_t, loff_t*); static struct file_operations aux_ops = { - owner: THIS_MODULE, + OWNER_THIS_MODULE /* FIXME: should have custom llseek with bounds checking */ read: mem_read, write: mem_write, @@ -735,18 +748,23 @@ static int mem_open(struct inode *inode, struct file *file) enum { rom, aux, ram } type; struct memdata *md; + V22_COMPAT_MOD_INC_USE_COUNT; + if (cid < PCILYNX_MINOR_AUX_START) { /* just for completeness */ + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } else if (cid < PCILYNX_MINOR_ROM_START) { cid -= PCILYNX_MINOR_AUX_START; if (cid >= num_of_cards || !cards[cid].aux_port) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = aux; } else if (cid < PCILYNX_MINOR_RAM_START) { cid -= PCILYNX_MINOR_ROM_START; if (cid >= num_of_cards || !cards[cid].local_rom) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = rom; @@ -755,6 +773,7 @@ static int mem_open(struct inode *inode, struct file *file) * It is currently used inside the driver! */ cid -= PCILYNX_MINOR_RAM_START; if (cid >= num_of_cards || !cards[cid].local_ram) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = ram; @@ -762,6 +781,7 @@ static int mem_open(struct inode *inode, struct file *file) md = (struct memdata *)vmalloc(sizeof(struct memdata)); if (md == NULL) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -793,6 +813,7 @@ static int mem_release(struct inode *inode, struct file *file) vfree(md); + V22_COMPAT_MOD_DEC_USE_COUNT; return 0; } @@ -801,16 +822,15 @@ static unsigned int aux_poll(struct file *file, poll_table *pt) struct memdata *md = (struct memdata *)file->private_data; int cid = md->cid; unsigned int mask; - int intr_seen; /* reading and writing is always allowed */ mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; if (md->type == aux) { poll_wait(file, &cards[cid].aux_intr_wait, pt); - intr_seen = atomic_read(&cards[cid].aux_intr_seen); - if (atomic_read(&md->aux_intr_last_seen) != intr_seen) { + if (atomic_read(&md->aux_intr_last_seen) + != atomic_read(&cards[cid].aux_intr_seen)) { mask |= POLLPRI; atomic_inc(&md->aux_intr_last_seen); } @@ -956,7 +976,7 @@ static ssize_t mem_read(struct file *file, char *buffer, size_t count, } while (bcount >= 4) { - retval = mem_dmaread(md, virt_to_phys(md->lynx->mem_dma_buffer) + retval = mem_dmaread(md, md->lynx->mem_dma_buffer_dma + count - bcount, bcount, off); if (retval < 0) return retval; @@ -1008,7 +1028,7 @@ static ssize_t mem_write(struct file *file, const char *buffer, size_t count, file->f_pos += count; return count; } - +#endif /* CONFIG_IEEE1394_PCILYNX_PORTS */ /******************************************************** @@ -1028,6 +1048,7 @@ static void lynx_irq_handler(int irq, void *dev_id, reg_write(lynx, LINK_INT_STATUS, linkint); //printk("-%d- one interrupt: 0x%08x / 0x%08x\n", lynx->id, intmask, linkint); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (intmask & PCI_INT_AUX_INT) { atomic_inc(&lynx->aux_intr_seen); wake_up_interruptible(&lynx->aux_intr_wait); @@ -1036,6 +1057,7 @@ static void lynx_irq_handler(int irq, void *dev_id, if (intmask & PCI_INT_DMA0_HLT) { wake_up_interruptible(&lynx->mem_dma_intr_wait); } +#endif if (intmask & PCI_INT_1394) { @@ -1108,17 +1130,24 @@ static void lynx_irq_handler(int irq, void *dev_id, u32 ack; struct hpsb_packet *packet; - spin_lock(&lynx->async_queue_lock); + spin_lock(&lynx->async.queue_lock); ack = reg_read(lynx, DMA3_CHAN_STAT); - packet = lynx->async_queue; - lynx->async_queue = packet->xnext; + packet = lynx->async.queue; + lynx->async.queue = packet->xnext; + + pci_unmap_single(lynx->dev, lynx->async.header_dma, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + pci_unmap_single(lynx->dev, lynx->async.data_dma, + packet->data_size, PCI_DMA_TODEVICE); + } - if (lynx->async_queue != NULL) { + if (lynx->async.queue != NULL) { send_next_async(lynx); } - spin_unlock(&lynx->async_queue_lock); + spin_unlock(&lynx->async.queue_lock); if (ack & DMA_CHAN_STAT_SPECIALACK) { ack = (ack >> 15) & 0xf; @@ -1150,7 +1179,7 @@ static void lynx_irq_handler(int irq, void *dev_id, || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) { cpu_to_be32s(q_data + 3); } - hpsb_packet_received(host, q_data, stat & 0x1fff); + hpsb_packet_received(host, q_data, stat & 0x1fff, 0); } run_pcl(lynx, lynx->rcv_pcl_start, 1); @@ -1184,7 +1213,8 @@ static void iso_rcv_bh(struct ti_lynx *lynx) "iso receive error on %d to 0x%p", idx, data); } else { hpsb_packet_received(lynx->host, data, - lynx->iso_rcv.stat[idx] & 0x1fff); + lynx->iso_rcv.stat[idx] & 0x1fff, + 0); } spin_lock_irqsave(&lynx->iso_rcv.lock, flags); @@ -1210,12 +1240,11 @@ static int add_card(struct pci_dev *dev) } while (0) struct ti_lynx *lynx; /* shortcut to currently handled device */ - unsigned long page; unsigned int i; if (num_of_cards == MAX_PCILYNX_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " - "Adjust MAX_PCILYNX_CARDS in ti_pcilynx.h.", + "Adjust MAX_PCILYNX_CARDS in pcilynx.h.", MAX_PCILYNX_CARDS); return 1; } @@ -1225,6 +1254,10 @@ static int add_card(struct pci_dev *dev) lynx->id = num_of_cards-1; lynx->dev = dev; + if (!pci_dma_supported(dev, 0xffffffff)) { + FAIL("DMA address limits not supported for PCILynx hardware %d", + lynx->id); + } if (pci_enable_device(dev)) { FAIL("failed to enable PCILynx hardware %d", lynx->id); } @@ -1239,61 +1272,50 @@ static int add_card(struct pci_dev *dev) } #ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM - lynx->pcl_mem = kmalloc(8 * sizeof(lynx->pcl_bmap) - * sizeof(struct ti_pcl), GFP_KERNEL); + lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE, + &lynx->pcl_mem_dma); if (lynx->pcl_mem != NULL) { lynx->state = have_pcl_mem; PRINT(KERN_INFO, lynx->id, - "allocated PCL memory %d Bytes @ 0x%p", - 8 * sizeof(lynx->pcl_bmap) * sizeof(struct ti_pcl), + "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE, lynx->pcl_mem); } else { FAIL("failed to allocate PCL memory area"); } #endif - lynx->mem_dma_buffer = kmalloc(65536, GFP_KERNEL); - if (lynx->mem_dma_buffer != NULL) { - lynx->state = have_aux_buf; - } else { +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + lynx->mem_dma_buffer = pci_alloc_consistent(dev, 65536, + &lynx->mem_dma_buffer_dma); + if (lynx->mem_dma_buffer == NULL) { FAIL("failed to allocate DMA buffer for aux"); } + lynx->state = have_aux_buf; +#endif - page = get_free_page(GFP_KERNEL); - if (page != 0) { - lynx->rcv_page = (void *)page; - lynx->state = have_1394_buffers; - } else { + lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->rcv_page_dma); + if (lynx->rcv_page == NULL) { FAIL("failed to allocate receive buffer"); } + lynx->state = have_1394_buffers; for (i = 0; i < ISORCV_PAGES; i++) { - page = get_free_page(GFP_KERNEL); - if (page != 0) { - lynx->iso_rcv.page[i] = (void *)page; - } else { + lynx->iso_rcv.page[i] = + pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->iso_rcv.page_dma[i]); + if (lynx->iso_rcv.page[i] == NULL) { FAIL("failed to allocate iso receive buffers"); } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) - lynx->registers = ioremap_nocache(dev->base_address[0], - PCILYNX_MAX_REGISTER); - lynx->local_ram = ioremap(dev->base_address[1], PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(dev->base_address[2], PCILYNX_MAX_MEMORY); -#else lynx->registers = ioremap_nocache(pci_resource_start(dev,0), PCILYNX_MAX_REGISTER); lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,15) - lynx->local_rom = ioremap(dev->rom_address, PCILYNX_MAX_MEMORY); -#else + lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), PCILYNX_MAX_MEMORY); -#endif lynx->state = have_iomappings; if (lynx->registers == NULL) { @@ -1309,15 +1331,17 @@ static int add_card(struct pci_dev *dev) /* alloc_pcl return values are not checked, it is expected that the * provided PCL space is sufficient for the initial allocations */ +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (lynx->aux_port != NULL) { lynx->dmem_pcl = alloc_pcl(lynx); aux_setup_pcls(lynx); sema_init(&lynx->mem_dma_mutex, 1); } +#endif lynx->rcv_pcl = alloc_pcl(lynx); lynx->rcv_pcl_start = alloc_pcl(lynx); - lynx->async_pcl = alloc_pcl(lynx); - lynx->async_pcl_start = alloc_pcl(lynx); + lynx->async.pcl = alloc_pcl(lynx); + lynx->async.pcl_start = alloc_pcl(lynx); for (i = 0; i < NUM_ISORCV_PCL; i++) { lynx->iso_rcv.pcl[i] = alloc_pcl(lynx); @@ -1330,8 +1354,10 @@ static int add_card(struct pci_dev *dev) reg_write(lynx, PCI_INT_ENABLE, PCI_INT_AUX_INT | PCI_INT_DMA_ALL); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS init_waitqueue_head(&lynx->mem_dma_intr_wait); init_waitqueue_head(&lynx->aux_intr_wait); +#endif lynx->iso_rcv.tq.routine = (void (*)(void*))iso_rcv_bh; lynx->iso_rcv.tq.data = lynx; @@ -1372,15 +1398,22 @@ static void remove_card(struct ti_lynx *lynx) case have_1394_buffers: for (i = 0; i < ISORCV_PAGES; i++) { if (lynx->iso_rcv.page[i]) { - free_page((unsigned long)lynx->iso_rcv.page[i]); + pci_free_consistent(lynx->dev, PAGE_SIZE, + lynx->iso_rcv.page[i], + lynx->iso_rcv.page_dma[i]); } } - free_page((unsigned long)lynx->rcv_page); + pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, + lynx->rcv_page_dma); case have_aux_buf: - kfree(lynx->mem_dma_buffer); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer, + lynx->mem_dma_buffer_dma); +#endif case have_pcl_mem: #ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM - kfree(lynx->pcl_mem); + pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, + lynx->pcl_mem_dma); #endif case have_intr: free_irq(lynx->dev->irq, lynx); @@ -1416,11 +1449,13 @@ static int init_driver() return -ENXIO; } +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) { PRINT_G(KERN_ERR, "allocation of char major number %d failed", PCILYNX_MAJOR); return -EBUSY; } +#endif return 0; } diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h index cf45d8d0a..ccb5a9f0b 100644 --- a/drivers/ieee1394/pcilynx.h +++ b/drivers/ieee1394/pcilynx.h @@ -12,7 +12,7 @@ #define PCI_DEVICE_ID_TI_PCILYNX 0x8000 #define MAX_PCILYNX_CARDS 4 -#define LOCALRAM_SIZE 64 +#define LOCALRAM_SIZE 4096 #define NUM_ISORCV_PCL 4 #define MAX_ISORCV_SIZE 2048 @@ -50,23 +50,27 @@ struct ti_lynx { void *aux_port; +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS atomic_t aux_intr_seen; wait_queue_head_t aux_intr_wait; void *mem_dma_buffer; + dma_addr_t mem_dma_buffer_dma; struct semaphore mem_dma_mutex; wait_queue_head_t mem_dma_intr_wait; +#endif /* - * use local RAM of LOCALRAM_SIZE (in kB) for PCLs, which allows for + * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes); * the following is an allocation bitmap */ - u8 pcl_bmap[LOCALRAM_SIZE]; + u8 pcl_bmap[LOCALRAM_SIZE / 1024]; -#ifndef CONFIG_IEEE1394_LYNXRAM +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM /* point to PCLs memory area if needed */ void *pcl_mem; + dma_addr_t pcl_mem_dma; #endif /* PCLs for local mem / aux transfers */ @@ -81,16 +85,21 @@ struct ti_lynx { pcl_t rcv_pcl_start, rcv_pcl; void *rcv_page; + dma_addr_t rcv_page_dma; int rcv_active; - pcl_t async_pcl_start, async_pcl; - struct hpsb_packet *async_queue; - spinlock_t async_queue_lock; + struct { + pcl_t pcl_start, pcl; + struct hpsb_packet *queue; + spinlock_t queue_lock; + dma_addr_t header_dma, data_dma; + } async; struct { pcl_t pcl[NUM_ISORCV_PCL]; u32 stat[NUM_ISORCV_PCL]; void *page[ISORCV_PAGES]; + dma_addr_t page_dma[ISORCV_PAGES]; pcl_t pcl_start; int chan_count; int next, last, used, running; @@ -381,11 +390,7 @@ inline static void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) - return lynx->dev->base_address[1] + pclid * sizeof(struct ti_pcl); -#else - return lynx->dev->resource[1].start + pclid * sizeof(struct ti_pcl); -#endif + return pci_resource_start(lynx->dev, 1) + pclid * sizeof(struct ti_pcl); } #else /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ @@ -407,7 +412,7 @@ inline static void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) { - return virt_to_bus(lynx->pcl_mem) + pclid * sizeof(struct ti_pcl); + return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl); } #endif /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 523f5d393..83c1169fe 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -4,6 +4,9 @@ * Raw interface to the bus * * Copyright (C) 1999, 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/kernel.h> @@ -13,8 +16,13 @@ #include <linux/fs.h> #include <linux/poll.h> #include <linux/module.h> +#include <linux/version.h> #include <asm/uaccess.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) +#include <linux/devfs_fs_kernel.h> +#endif + #include "ieee1394.h" #include "ieee1394_types.h" #include "ieee1394_core.h" @@ -24,6 +32,8 @@ #include "raw1394.h" +static devfs_handle_t devfs_handle = NULL; + LIST_HEAD(host_info_list); static int host_count = 0; spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; @@ -257,7 +267,7 @@ static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data, req->req.type = RAW1394_REQ_ISO_RECEIVE; req->req.generation = get_hpsb_generation(); req->req.misc = 0; - req->req.recvb = fi->iso_buffer; + req->req.recvb = (u64)fi->iso_buffer; req->req.length = MIN(length, fi->iso_buffer_length); list_add_tail(&req->list, &reqs); @@ -326,7 +336,7 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction, req->req.type = RAW1394_REQ_FCP_REQUEST; req->req.generation = get_hpsb_generation(); req->req.misc = nodeid | (direction << 16); - req->req.recvb = (quadlet_t *)fi->fcp_buffer; + req->req.recvb = (u64)fi->fcp_buffer; req->req.length = length; list_add_tail(&req->list, &reqs); @@ -343,7 +353,7 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction, } -static int dev_read(struct file *file, char *buffer, size_t count, +static ssize_t dev_read(struct file *file, char *buffer, size_t count, loff_t *offset_is_ignored) { struct file_info *fi = (struct file_info *)file->private_data; @@ -376,7 +386,8 @@ static int dev_read(struct file *file, char *buffer, size_t count, req = list_entry(lh, struct pending_request, list); if (req->req.length) { - if (copy_to_user(req->req.recvb, req->data, req->req.length)) { + if (copy_to_user((void *)req->req.recvb, req->data, + req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; } } @@ -507,7 +518,7 @@ static void handle_iso_listen(struct file_info *fi, struct pending_request *req) } else { fi->listen_channels |= 1ULL << channel; hpsb_listen_channel(hl_handle, fi->host, channel); - fi->iso_buffer = req->req.recvb; + fi->iso_buffer = (void *)req->req.recvb; fi->iso_buffer_length = req->req.length; } } else { @@ -563,7 +574,7 @@ static int handle_local_request(struct file_info *fi, break; case RAW1394_REQ_ASYNC_WRITE: - if (copy_from_user(req->data, req->req.sendb, + if (copy_from_user(req->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -588,7 +599,7 @@ static int handle_local_request(struct file_info *fi, } } - if (copy_from_user(req->data, req->req.sendb, + if (copy_from_user(req->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -646,7 +657,7 @@ static int handle_remote_request(struct file_info *fi, if (req->req.length == 4) { quadlet_t x; - if (copy_from_user(&x, req->req.sendb, 4)) { + if (copy_from_user(&x, (void *)req->req.sendb, 4)) { req->req.error = RAW1394_ERROR_MEMFAULT; } @@ -658,7 +669,7 @@ static int handle_remote_request(struct file_info *fi, req->req.length); if (!packet) return -ENOMEM; - if (copy_from_user(packet->data, req->req.sendb, + if (copy_from_user(packet->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; } @@ -684,7 +695,7 @@ static int handle_remote_request(struct file_info *fi, req->req.misc); if (!packet) return -ENOMEM; - if (copy_from_user(packet->data, req->req.sendb, + if (copy_from_user(packet->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -761,12 +772,12 @@ static int state_connected(struct file_info *fi, struct pending_request *req) } -static int dev_write(struct file *file, const char *buffer, size_t count, +static ssize_t dev_write(struct file *file, const char *buffer, size_t count, loff_t *offset_is_ignored) { struct file_info *fi = (struct file_info *)file->private_data; struct pending_request *req; - int retval = 0; + ssize_t retval = 0; if (count != sizeof(struct raw1394_request)) { return -EINVAL; @@ -828,8 +839,11 @@ static int dev_open(struct inode *inode, struct file *file) return -ENXIO; } + V22_COMPAT_MOD_INC_USE_COUNT; + fi = kmalloc(sizeof(struct file_info), SLAB_KERNEL); if (fi == NULL) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -898,6 +912,7 @@ static int dev_release(struct inode *inode, struct file *file) kfree(fi); + V22_COMPAT_MOD_DEC_USE_COUNT; return 0; } @@ -910,7 +925,7 @@ static struct hpsb_highlevel_ops hl_ops = { }; static struct file_operations file_ops = { - owner: THIS_MODULE, + OWNER_THIS_MODULE read: dev_read, write: dev_write, poll: dev_poll, @@ -926,18 +941,24 @@ int init_raw1394(void) return -ENOMEM; } - if (register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, - &file_ops)) { - HPSB_ERR("raw1394 failed to allocate device major"); + devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE, + RAW1394_DEVICE_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &file_ops, + NULL); + + if (devfs_register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, + &file_ops)) { + HPSB_ERR("raw1394 failed to register /dev/raw1394 device"); return -EBUSY; } - + printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME); return 0; } void cleanup_raw1394(void) { - unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME); + devfs_unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME); + devfs_unregister(devfs_handle); hpsb_unregister_highlevel(hl_handle); } diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h index f23bfc051..b0d819429 100644 --- a/drivers/ieee1394/raw1394.h +++ b/drivers/ieee1394/raw1394.h @@ -1,11 +1,10 @@ - #ifndef IEEE1394_RAW1394_H #define IEEE1394_RAW1394_H #define RAW1394_DEVICE_MAJOR 171 #define RAW1394_DEVICE_NAME "raw1394" -#define RAW1394_KERNELAPI_VERSION 2 +#define RAW1394_KERNELAPI_VERSION 3 /* state: opened */ #define RAW1394_REQ_INITIALIZE 1 @@ -45,24 +44,27 @@ #define RAW1394_ERROR_TIMEOUT (-1102) +#include <asm/types.h> + struct raw1394_request { - int type; - int error; - int misc; + __u32 type; + __s32 error; + __u32 misc; + + __u32 generation; + __u32 length; - unsigned int generation; - octlet_t address; + __u64 address; - unsigned long tag; + __u64 tag; - size_t length; - quadlet_t *sendb; - quadlet_t *recvb; + __u64 sendb; + __u64 recvb; }; struct raw1394_khost_list { - int nodes; - char name[32]; + __u32 nodes; + __u8 name[32]; }; #ifdef __KERNEL__ diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c new file mode 100644 index 000000000..9f00fd3f0 --- /dev/null +++ b/drivers/ieee1394/video1394.c @@ -0,0 +1,1266 @@ +/* + * video1394.c - video driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> + * + * 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/config.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <asm/byteorder.h> +#include <asm/atomic.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/proc_fs.h> +#include <linux/tqueue.h> +#include <linux/delay.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <linux/sched.h> +#include <asm/segment.h> +#include <linux/types.h> +#include <linux/wrapper.h> +#include <linux/vmalloc.h> + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "video1394.h" + +#include "ohci1394.h" + +#define VIDEO1394_MAJOR 172 +#define ISO_CHANNELS 64 +#define ISO_RECEIVE 0 +#define ISO_TRANSMIT 1 + +struct it_dma_prg { + struct dma_cmd begin; + quadlet_t data[4]; + struct dma_cmd end; + quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ +}; + +struct dma_iso_ctx { + struct ti_ohci *ohci; + int ctx; + int channel; + int last_buffer; + unsigned int num_desc; + unsigned int buf_size; + unsigned int frame_size; + unsigned int packet_size; + unsigned int left_size; + unsigned int nb_cmd; + unsigned char *buf; + struct dma_cmd **ir_prg; + struct it_dma_prg **it_prg; + unsigned int *buffer_status; + int ctrlClear; + int ctrlSet; + int cmdPtr; + int ctxMatch; + wait_queue_head_t waitq; +}; + +struct video_card { + struct ti_ohci *ohci; + + struct dma_iso_ctx **ir_context; + struct dma_iso_ctx **it_context; + struct dma_iso_ctx *current_ctx; +}; + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define VIDEO1394_DEBUG +#endif + +#ifdef DBGMSG +#undef DBGMSG +#endif + +#ifdef VIDEO1394_DEBUG +#define DBGMSG(card, fmt, args...) \ +printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) +#else +#define DBGMSG(card, fmt, args...) +#endif + +/* print general (card independent) information */ +#define PRINT_G(level, fmt, args...) \ +printk(level "video1394: " fmt "\n" , ## args) + +/* print card specific information */ +#define PRINT(level, card, fmt, args...) \ +printk(level "video1394_%d: " fmt "\n" , card , ## args) + +void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent); + +static struct video_card video_cards[MAX_OHCI1394_CARDS]; +static int num_of_video_cards = 0; +static struct video_template video_tmpl = { irq_handler }; + +/* Taken from bttv.c */ +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define page_address(x) (x) +#endif + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) + ret = (page_address(pte_page(pte))| + (adr&(PAGE_SIZE-1))); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, + no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +static int free_dma_iso_ctx(struct dma_iso_ctx **d) +{ + int i; + struct ti_ohci *ohci; + + if ((*d)==NULL) return -1; + + ohci = (struct ti_ohci *)(*d)->ohci; + + DBGMSG(ohci->id, "Freeing dma_iso_ctx %d", (*d)->ctx); + + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->buf) rvfree((void *)(*d)->buf, + (*d)->num_desc * (*d)->buf_size); + + if ((*d)->ir_prg) { + for (i=0;i<(*d)->num_desc;i++) + if ((*d)->ir_prg[i]) kfree((*d)->ir_prg[i]); + kfree((*d)->ir_prg); + } + + if ((*d)->it_prg) { + for (i=0;i<(*d)->num_desc;i++) + if ((*d)->it_prg[i]) kfree((*d)->it_prg[i]); + kfree((*d)->it_prg); + } + + if ((*d)->buffer_status) + kfree((*d)->buffer_status); + + kfree(*d); + *d = NULL; + + return 0; +} + +static struct dma_iso_ctx * +alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc, + int buf_size, int channel, unsigned int packet_size) +{ + struct dma_iso_ctx *d=NULL; + int i; + + d = (struct dma_iso_ctx *)kmalloc(sizeof(struct dma_iso_ctx), + GFP_KERNEL); + memset(d, 0, sizeof(struct dma_iso_ctx)); + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma_iso_ctx"); + return NULL; + } + + d->ohci = (void *)ohci; + d->ctx = ctx; + d->channel = channel; + d->num_desc = num_desc; + d->frame_size = buf_size; + if (buf_size%PAGE_SIZE) + d->buf_size = buf_size + PAGE_SIZE - (buf_size%PAGE_SIZE); + else + d->buf_size = buf_size; + d->last_buffer = -1; + d->buf = NULL; + d->ir_prg = NULL; + init_waitqueue_head(&d->waitq); + + d->buf = rvmalloc(d->num_desc * d->buf_size); + + if (d->buf == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->buf, 0, d->num_desc * d->buf_size); + + if (type == ISO_RECEIVE) { + d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx; + d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx; + d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx; + d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx; + + d->ir_prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *), + GFP_KERNEL); + + if (d->ir_prg == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->ir_prg, 0, d->num_desc * sizeof(struct dma_cmd *)); + + d->nb_cmd = d->buf_size / PAGE_SIZE + 1; + d->left_size = (d->frame_size % PAGE_SIZE) ? + d->frame_size % PAGE_SIZE : PAGE_SIZE; + + for (i=0;i<d->num_desc;i++) { + d->ir_prg[i] = kmalloc(d->nb_cmd * + sizeof(struct dma_cmd), + GFP_KERNEL); + if (d->ir_prg[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + } + } + else { /* ISO_TRANSMIT */ + d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx; + d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx; + d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx; + + d->it_prg = kmalloc(d->num_desc * sizeof(struct it_dma_prg *), + GFP_KERNEL); + + if (d->it_prg == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma it prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->it_prg, 0, d->num_desc*sizeof(struct it_dma_prg *)); + + d->packet_size = packet_size; + + if (PAGE_SIZE % packet_size || packet_size>2048) { + PRINT(KERN_ERR, ohci->id, + "Packet size %d not yet supported\n", + packet_size); + free_dma_iso_ctx(&d); + return NULL; + } + + d->nb_cmd = d->frame_size / d->packet_size; + if (d->frame_size % d->packet_size) { + d->nb_cmd++; + d->left_size = d->frame_size % d->packet_size; + } + else + d->left_size = d->packet_size; + + for (i=0;i<d->num_desc;i++) { + d->it_prg[i] = kmalloc(d->nb_cmd * + sizeof(struct it_dma_prg), + GFP_KERNEL); + if (d->it_prg[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma it prg"); + free_dma_iso_ctx(&d); + return NULL; + } + } + } + + d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), + GFP_KERNEL); + + if (d->buffer_status == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); + + PRINT(KERN_INFO, ohci->id, "Iso %s DMA: %d buffers " + "of size %d allocated for a frame size %d, each with %d prgs", + (type==ISO_RECEIVE) ? "receive" : "transmit", + d->num_desc, d->buf_size, d->frame_size, d->nb_cmd); + + return d; +} + +static void reset_ir_status(struct dma_iso_ctx *d, int n) +{ + int i; + d->ir_prg[n][0].status = 4; + d->ir_prg[n][1].status = PAGE_SIZE-4; + for (i=2;i<d->nb_cmd-1;i++) + d->ir_prg[n][i].status = PAGE_SIZE; + d->ir_prg[n][i].status = d->left_size; +} + +static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n) +{ + struct dma_cmd *ir_prg = d->ir_prg[n]; + unsigned long buf = (unsigned long)d->buf+n*d->buf_size; + int i; + + /* the first descriptor will sync and read only 4 bytes */ + ir_prg[0].control = (0x280F << 16) | 4; + ir_prg[0].address = kvirt_to_bus(buf); + ir_prg[0].branchAddress = (virt_to_bus(&(ir_prg[1].control)) + & 0xfffffff0) | 0x1; + + /* the second descriptor will read PAGE_SIZE-4 bytes */ + ir_prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); + ir_prg[1].address = kvirt_to_bus(buf+4); + ir_prg[1].branchAddress = (virt_to_bus(&(ir_prg[2].control)) + & 0xfffffff0) | 0x1; + + for (i=2;i<d->nb_cmd-1;i++) { + ir_prg[i].control = (0x280C << 16) | PAGE_SIZE; + ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); + + ir_prg[i].branchAddress = + (virt_to_bus(&(ir_prg[i+1].control)) + & 0xfffffff0) | 0x1; + } + + /* the last descriptor will generate an interrupt */ + ir_prg[i].control = (0x283C << 16) | d->left_size; + ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); +} + +static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;i<d->num_desc;i++) { + initialize_dma_ir_prg(d, i); + reset_ir_status(d, i); + } + + /* Set bufferFill, no header */ + reg_write(ohci, d->ctrlSet, 0x80000000); + + /* Set the context match register to match on all tags, + sync for sync tag, and listen to d->channel */ + reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); +} + +/* find which context is listening to this channel */ +int ir_ctx_listening(struct video_card *video, int channel) +{ + int i; + struct ti_ohci *ohci = video->ohci; + + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]) { + if (video->ir_context[i]->channel==channel) + return i; + } + + PRINT(KERN_ERR, ohci->id, + "no iso context is listening to channel %d", + channel); + return -1; +} + +int it_ctx_talking(struct video_card *video, int channel) +{ + int i; + struct ti_ohci *ohci = video->ohci; + + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]) { + if (video->it_context[i]->channel==channel) + return i; + } + + PRINT(KERN_ERR, ohci->id, + "no iso context is talking to channel %d", + channel); + return -1; +} + +int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) +{ + int i; + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "Iso receive event received but " + "context not allocated"); + return -EFAULT; + } + + for (i=0;i<d->num_desc;i++) { + if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) { + reset_ir_status(d, i); + d->buffer_status[i] = VIDEO1394_BUFFER_READY; + } + } + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + return 0; +} + +int wakeup_dma_it_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) +{ + int i; + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "Iso transmit event received but " + "context not allocated"); + return -EFAULT; + } + + for (i=0;i<d->num_desc;i++) { + if (d->it_prg[i][d->nb_cmd-1].end.status & 0xFFFF0000) { + d->it_prg[i][d->nb_cmd-1].end.status = 0; + d->buffer_status[i] = VIDEO1394_BUFFER_READY; + } + } + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + return 0; +} + +static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag) +{ + struct it_dma_prg *it_prg = d->it_prg[n]; + unsigned long buf = (unsigned long)d->buf+n*d->buf_size; + int i; + + for (i=0;i<d->nb_cmd;i++) { + + it_prg[i].begin.control = OUTPUT_MORE_IMMEDIATE | 8 ; + it_prg[i].begin.address = 0; + + it_prg[i].begin.status = 0; + + /* FIXME: what is the tag value + speed selection */ + it_prg[i].data[0] = + (DMA_SPEED_400<<16) | (d->channel<<8) | 0xa0; + if (i==0) it_prg[i].data[0] |= sync_tag; + it_prg[i].data[1] = d->packet_size << 16; + it_prg[i].data[2] = 0; + it_prg[i].data[3] = 0; + + it_prg[i].end.control = 0x100c0000; + it_prg[i].end.address = + kvirt_to_bus(buf+i*d->packet_size); + + if (i<d->nb_cmd-1) { + it_prg[i].end.control |= d->packet_size; + it_prg[i].begin.branchAddress = + (virt_to_bus(&(it_prg[i+1].begin.control)) + & 0xfffffff0) | 0x3; + it_prg[i].end.branchAddress = + (virt_to_bus(&(it_prg[i+1].begin.control)) + & 0xfffffff0) | 0x3; + } + else { + /* the last prg generates an interrupt */ + it_prg[i].end.control |= 0x08300000 | d->left_size; + /* the last prg doesn't branch */ + it_prg[i].begin.branchAddress = 0; + it_prg[i].end.branchAddress = 0; + } + it_prg[i].end.status = 0; + +#if 0 + printk("%d:%d: %08x-%08x ctrl %08x brch %08x d0 %08x d1 %08x\n",n,i, + virt_to_bus(&(it_prg[i].begin.control)), + virt_to_bus(&(it_prg[i].end.control)), + it_prg[i].end.control, + it_prg[i].end.branchAddress, + it_prg[i].data[0], it_prg[i].data[1]); +#endif + } +} + +static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;i<d->num_desc;i++) + initialize_dma_it_prg(d, i, sync_tag); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx); +} + +static int do_iso_mmap(struct ti_ohci *ohci, struct dma_iso_ctx *d, + const char *adr, unsigned long size) +{ + unsigned long start=(unsigned long) adr; + unsigned long page,pos; + + if (size>d->num_desc * d->buf_size) { + PRINT(KERN_ERR, ohci->id, + "iso context %d buf size is different from mmap size", + d->ctx); + return -EINVAL; + } + if (!d->buf) { + PRINT(KERN_ERR, ohci->id, + "iso context %d is not allocated", d->ctx); + return -EINVAL; + } + + pos=(unsigned long) d->buf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; +} + +static int video1394_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_card *video = &video_cards[MINOR(inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + + switch(cmd) + { + case VIDEO1394_LISTEN_CHANNEL: + case VIDEO1394_TALK_CHANNEL: + { + struct video1394_mmap v; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { + PRINT(KERN_ERR, ohci->id, + "iso channel %d out of bound", v.channel); + return -EFAULT; + } + if (test_and_set_bit(v.channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is already taken", v.channel); + return -EFAULT; + } + + if (v.buf_size<=0) { + PRINT(KERN_ERR, ohci->id, + "Invalid %d length buffer requested",v.buf_size); + return -EFAULT; + } + + if (v.nb_buffers<=0) { + PRINT(KERN_ERR, ohci->id, + "Invalid %d buffers requested",v.nb_buffers); + return -EFAULT; + } + + if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->id, + "%d buffers of size %d bytes is too big", + v.nb_buffers, v.buf_size); + return -EFAULT; + } + + if (cmd == VIDEO1394_LISTEN_CHANNEL) { + /* find a free iso receive context */ + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]==NULL) break; + + if (i==(ohci->nb_iso_rcv_ctx-1)) { + PRINT(KERN_ERR, ohci->id, + "no iso context available"); + return -EFAULT; + } + + video->ir_context[i] = + alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1, + v.nb_buffers, v.buf_size, + v.channel, 0); + + if (video->ir_context[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "Couldn't allocate ir context"); + return -EFAULT; + } + initialize_dma_ir_ctx(video->ir_context[i], + v.sync_tag); + + video->current_ctx = video->ir_context[i]; + + v.buf_size = video->ir_context[i]->buf_size; + + PRINT(KERN_INFO, ohci->id, + "iso context %d listen on channel %d", i+1, + v.channel); + } + else { + /* find a free iso transmit context */ + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]==NULL) break; + + if (i==ohci->nb_iso_xmit_ctx) { + PRINT(KERN_ERR, ohci->id, + "no iso context available"); + return -EFAULT; + } + + video->it_context[i] = + alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i, + v.nb_buffers, v.buf_size, + v.channel, v.packet_size); + + if (video->it_context[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "Couldn't allocate it context"); + return -EFAULT; + } + initialize_dma_it_ctx(video->it_context[i], + v.sync_tag); + + video->current_ctx = video->it_context[i]; + + v.buf_size = video->it_context[i]->buf_size; + + PRINT(KERN_INFO, ohci->id, + "iso context %d talk on channel %d", i, + v.channel); + } + + if(copy_to_user((void *)arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDEO1394_UNLISTEN_CHANNEL: + case VIDEO1394_UNTALK_CHANNEL: + { + int channel; + int i; + + if(copy_from_user(&channel, (void *)arg, sizeof(int))) + return -EFAULT; + + if (!test_and_clear_bit(channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", channel); + return -EFAULT; + } + + if (cmd == VIDEO1394_UNLISTEN_CHANNEL) { + i = ir_ctx_listening(video, channel); + if (i<0) return -EFAULT; + + free_dma_iso_ctx(&video->ir_context[i]); + + PRINT(KERN_INFO, ohci->id, + "iso context %d stop listening on channel %d", + i+1, channel); + } + else { + i = it_ctx_talking(video, channel); + if (i<0) return -EFAULT; + + free_dma_iso_ctx(&video->it_context[i]); + + PRINT(KERN_INFO, ohci->id, + "iso context %d stop talking on channel %d", + i, channel); + } + + return 0; + } + case VIDEO1394_LISTEN_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = ir_ctx_listening(video, v.channel); + if (i<0) return -EFAULT; + d = video->ir_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->id, + "buffer %d is already used",v.buffer); + return -EFAULT; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + if (d->last_buffer>=0) + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = + (virt_to_bus(&(d->ir_prg[v.buffer][0].control)) + & 0xfffffff0) | 0x1; + + d->last_buffer = v.buffer; + + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0; + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + virt_to_bus(&(d->ir_prg[v.buffer][0]))|0x1); + + /* Run IR context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, ohci->id, + "Waking up iso dma ctx=%d", d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_LISTEN_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = ir_ctx_listening(video, v.channel); + if (i<0) return -EFAULT; + d = video->ir_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: +#if 1 + while(d->buffer_status[v.buffer]!= + VIDEO1394_BUFFER_READY) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + } +#else + if (wait_event_interruptible(d->waitq, + d->buffer_status[v.buffer] + == VIDEO1394_BUFFER_READY) + == -ERESTARTSYS) + return -EINTR; +#endif + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->id, + "buffer %d is not queued",v.buffer); + return -EFAULT; + } + } + case VIDEO1394_TALK_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = it_ctx_talking(video, v.channel); + if (i<0) return -EFAULT; + d = video->it_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->id, + "buffer %d is already used",v.buffer); + return -EFAULT; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + if (d->last_buffer>=0) { + d->it_prg[d->last_buffer] + [d->nb_cmd-1].end.branchAddress = + (virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) + & 0xfffffff0) | 0x3; + + d->it_prg[d->last_buffer] + [d->nb_cmd-1].begin.branchAddress = + (virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) + & 0xfffffff0) | 0x3; + } + d->last_buffer = v.buffer; + + d->it_prg[d->last_buffer][d->nb_cmd-1].end.branchAddress = 0; + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->id, "Starting iso transmit DMA ctx=%d", + d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + virt_to_bus(&(d->it_prg[v.buffer][0]))|0x3); + + /* Run IT context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, ohci->id, + "Waking up iso transmit dma ctx=%d", + d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_TALK_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = it_ctx_talking(video, v.channel); + if (i<0) return -EFAULT; + d = video->it_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: +#if 1 + while(d->buffer_status[v.buffer]!= + VIDEO1394_BUFFER_READY) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + } +#else + if (wait_event_interruptible(d->waitq, + d->buffer_status[v.buffer] + == VIDEO1394_BUFFER_READY) + == -ERESTARTSYS) + return -EINTR; +#endif + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->id, + "buffer %d is not queued",v.buffer); + return -EFAULT; + } + } + default: + return -EINVAL; + } +} + +/* + * This maps the vmalloced and reserved buffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +int video1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_card *video = + &video_cards[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + + PRINT(KERN_INFO, ohci->id, "mmap"); + if (video->current_ctx == NULL) { + PRINT(KERN_ERR, ohci->id, "current iso context not set"); + return -EINVAL; + } + + return do_iso_mmap(ohci, video->current_ctx, + (char *)vma->vm_start, + (unsigned long)(vma->vm_end-vma->vm_start)); + return 0; +} + +static int video1394_open(struct inode *inode, struct file *file) +{ + int i = MINOR(inode->i_rdev); + + if (i<0 || i>=num_of_video_cards) { + PRINT(KERN_ERR, i, "ohci card %d not found", i); + return -EIO; + } + + V22_COMPAT_MOD_INC_USE_COUNT; + + PRINT(KERN_INFO, i, "open"); + + return 0; +} + +static int video1394_release(struct inode *inode, struct file *file) +{ + struct video_card *video = &video_cards[MINOR(inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + int i; + + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]) { + if (!test_and_clear_bit( + video->ir_context[i]->channel, + &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", + video->ir_context[i]->channel); + } + PRINT(KERN_INFO, ohci->id, + "iso receive context %d stop listening " + "on channel %d", i+1, + video->ir_context[i]->channel); + free_dma_iso_ctx(&video->ir_context[i]); + } + + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]) { + if (!test_and_clear_bit( + video->it_context[i]->channel, + &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", + video->it_context[i]->channel); + } + PRINT(KERN_INFO, ohci->id, + "iso transmit context %d stop talking " + "on channel %d", i+1, + video->it_context[i]->channel); + free_dma_iso_ctx(&video->it_context[i]); + } + + + V22_COMPAT_MOD_DEC_USE_COUNT; + + PRINT(KERN_INFO, ohci->id, "release"); + return 0; +} + +void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent) +{ + int i; + struct video_card *video = &video_cards[card]; + + DBGMSG(card, "Iso event Recv: %08x Xmit: %08x", + isoRecvIntEvent, isoXmitIntEvent); + + for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++) + if (isoRecvIntEvent & (1<<(i+1))) + wakeup_dma_ir_ctx(video->ohci, + video->ir_context[i]); + + for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++) + if (isoXmitIntEvent & (1<<i)) + wakeup_dma_it_ctx(video->ohci, + video->it_context[i]); +} + +static struct file_operations video1394_fops= +{ + OWNER_THIS_MODULE + ioctl: video1394_ioctl, + mmap: video1394_mmap, + open: video1394_open, + release: video1394_release +}; + +static int video1394_init(int i, struct ti_ohci *ohci) +{ + struct video_card *video = &video_cards[i]; + + if (ohci1394_register_video(ohci, &video_tmpl)<0) { + PRINT(KERN_ERR, i, "register_video failed"); + return -1; + } + + video->ohci = ohci; + + /* Iso receive dma contexts */ + video->ir_context = (struct dma_iso_ctx **) + kmalloc((ohci->nb_iso_rcv_ctx-1)* + sizeof(struct dma_iso_ctx *), GFP_KERNEL); + if (video->ir_context) + memset(video->ir_context, 0, + (ohci->nb_iso_rcv_ctx-1)*sizeof(struct dma_iso_ctx *)); + else { + PRINT(KERN_ERR, ohci->id, "Cannot allocate ir_context"); + return -1; + } + + /* Iso transmit dma contexts */ + video->it_context = (struct dma_iso_ctx **) + kmalloc(ohci->nb_iso_xmit_ctx * + sizeof(struct dma_iso_ctx *), GFP_KERNEL); + if (video->it_context) + memset(video->it_context, 0, + ohci->nb_iso_xmit_ctx * sizeof(struct dma_iso_ctx *)); + else { + PRINT(KERN_ERR, ohci->id, "Cannot allocate it_context"); + return -1; + } + + return 0; +} + +static void remove_card(struct video_card *video) +{ + int i; + + ohci1394_unregister_video(video->ohci, &video_tmpl); + + /* Free the iso receive contexts */ + if (video->ir_context) { + for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++) { + free_dma_iso_ctx(&video->ir_context[i]); + } + kfree(video->ir_context); + } + + /* Free the iso transmit contexts */ + if (video->it_context) { + for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++) { + free_dma_iso_ctx(&video->it_context[i]); + } + kfree(video->it_context); + } +} + +#ifdef MODULE + +/* EXPORT_NO_SYMBOLS; */ + +MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>"); +MODULE_DESCRIPTION("driver for digital video on OHCI board"); +MODULE_SUPPORTED_DEVICE("video1394"); + +void cleanup_module(void) +{ + int i; + unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME); + + for (i=0; i<num_of_video_cards; i++) + remove_card(&video_cards[i]); + + printk(KERN_INFO "removed " VIDEO1394_DRIVER_NAME " module\n"); +} + +int init_module(void) +{ + struct ti_ohci *ohci; + int i; + + memset(video_cards, 0, MAX_OHCI1394_CARDS * sizeof(struct video_card)); + num_of_video_cards = 0; + + for (i=0; i<MAX_OHCI1394_CARDS; i++) { + ohci=ohci1394_get_struct(i); + if (ohci) { + num_of_video_cards++; + video1394_init(i, ohci); + } + } + + if (!num_of_video_cards) { + PRINT_G(KERN_INFO, "no ohci card found... init failed"); + return -EIO; + } + + if (register_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME, + &video1394_fops)) { + printk("video1394: unable to get major %d\n", + VIDEO1394_MAJOR); + return -EIO; + } + + PRINT_G(KERN_INFO, "initialized with %d ohci cards", + num_of_video_cards); + + return 0; +} + +#endif /* MODULE */ + + diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h index 47b8e64a2..dc6e5c981 100644 --- a/drivers/ieee1394/video1394.h +++ b/drivers/ieee1394/video1394.h @@ -20,6 +20,8 @@ #ifndef _VIDEO_1394_H #define _VIDEO_1394_H +#define VIDEO1394_DRIVER_NAME "video1394" + #define VIDEO1394_MAX_SIZE 0x400000 enum { @@ -31,8 +33,12 @@ enum { enum { VIDEO1394_LISTEN_CHANNEL = 0, VIDEO1394_UNLISTEN_CHANNEL, - VIDEO1394_QUEUE_BUFFER, - VIDEO1394_WAIT_BUFFER + VIDEO1394_LISTEN_QUEUE_BUFFER, + VIDEO1394_LISTEN_WAIT_BUFFER, + VIDEO1394_TALK_CHANNEL, + VIDEO1394_UNTALK_CHANNEL, + VIDEO1394_TALK_QUEUE_BUFFER, + VIDEO1394_TALK_WAIT_BUFFER }; struct video1394_mmap { @@ -40,6 +46,8 @@ struct video1394_mmap { int sync_tag; int nb_buffers; int buf_size; + int packet_size; + int fps; }; struct video1394_wait { @@ -47,4 +55,5 @@ struct video1394_wait { int buffer; }; -#endif + +#endif diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 610c20b6d..009010896 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -2089,7 +2089,7 @@ int capi_init(void) #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE devfs_unregister_chrdev(capi_rawmajor, "capi/r%d"); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - devfs_unregister(devfs_find_handle(NULL, "capi20", 0, + devfs_unregister(devfs_find_handle(NULL, "capi20", capi_major, 0, DEVFS_SPECIAL_CHR, 0)); return -EIO; @@ -2112,13 +2112,13 @@ int capi_init(void) for (j = 0; j < CAPINC_NR_PORTS; j++) { char devname[32]; sprintf(devname, "capi/r%u", j); - devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0)); } capinc_tty_exit(); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ (void) detach_capi_interface(&cuser); devfs_unregister_chrdev(capi_major, "capi20"); - devfs_unregister(devfs_find_handle(NULL, "capi20", 0, + devfs_unregister(devfs_find_handle(NULL, "capi20", capi_major, 0, DEVFS_SPECIAL_CHR, 0)); MOD_DEC_USE_COUNT; @@ -2144,7 +2144,7 @@ void cleanup_module(void) (void)proc_exit(); devfs_unregister_chrdev(capi_major, "capi20"); - devfs_unregister(devfs_find_handle(NULL, "isdn/capi20", 0, capi_major, 0, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, "isdn/capi20", capi_major, 0, DEVFS_SPECIAL_CHR, 0)); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE capinc_tty_exit(); @@ -2152,7 +2152,7 @@ void cleanup_module(void) for (j = 0; j < CAPINC_NR_PORTS; j++) { char devname[32]; sprintf(devname, "capi/r%u", j); - devfs_unregister(devfs_find_handle(NULL, devname, 0, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0)); + devfs_unregister(devfs_find_handle(NULL, devname, capi_rawmajor, j, DEVFS_SPECIAL_CHR, 0)); } #endif (void) detach_capi_interface(&cuser); diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index fa5a3d844..f6c326301 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -2603,7 +2603,7 @@ static void isdn_init_devfs(void) int i; # endif - devfs_handle = devfs_mk_dir (NULL, "isdn", 4, NULL); + devfs_handle = devfs_mk_dir (NULL, "isdn", NULL); # ifdef CONFIG_ISDN_PPP for (i = 0; i < ISDN_MAX_CHANNELS; i++) { char buf[8]; diff --git a/drivers/mtd/.cvsignore b/drivers/mtd/.cvsignore new file mode 100644 index 000000000..857dd22e9 --- /dev/null +++ b/drivers/mtd/.cvsignore @@ -0,0 +1,2 @@ +.depend +.*.flags diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in new file mode 100644 index 000000000..26fcfe517 --- /dev/null +++ b/drivers/mtd/Config.in @@ -0,0 +1,43 @@ +mainmenu_option next_comment +comment 'Memory Technology Devices (MTD)' + +tristate 'Memory Technology Device (MTD) support' CONFIG_MTD + +if [ "$CONFIG_MTD" != "n" ]; then + dep_tristate 'Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD + dep_tristate 'CFI support for Intel/Sharp Extended Command Set chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_CFI + dep_tristate 'CFI chips in virtual memory map' CONFIG_MTD_NORA $CONFIG_MTD_CFI + dep_tristate 'CFI chips in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI + dep_tristate 'CFI chips on RPXLite PPC board' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI + dep_tristate 'M-Systems Disk-On-Chip 1000 support' CONFIG_MTD_DOC1000 $CONFIG_MTD + dep_tristate 'M-Systems Disk-On-Chip 2000' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate 'M-Systems Disk-On-Chip Millennium' CONFIG_MTD_DOC2001 $CONFIG_MTD + if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then + define_bool CONFIG_MTD_DOCPROBE y + else + if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then + define_bool CONFIG_MTD_DOCPROBE m + else + define_bool CONFIG_MTD_DOCPROBE n + fi + fi + dep_tristate 'Use extra onboard system memory as MTD device' CONFIG_MTD_SLRAM $CONFIG_MTD + dep_tristate 'Octagon 5066 SBC onboard flash support' CONFIG_MTD_OCTAGON $CONFIG_MTD + dep_tristate 'Tempustech VMAX SBC301 onboard flash support' CONFIG_MTD_VMAX $CONFIG_MTD + dep_tristate 'Mixcom piggyback flash card support' CONFIG_MTD_MIXMEM $CONFIG_MTD + dep_tristate 'Ramix PMC551 PCI Mezzanine ram card support' CONFIG_MTD_PMC551 $CONFIG_MTD + if [ "$CONFIG_MTD_PMC551" != "n" ]; then + bool 'PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX + fi + dep_tristate 'Debugging RAM test driver' CONFIG_MTD_MTDRAM $CONFIG_MTD + + dep_tristate 'FTL (Flash Translation Layer) support' CONFIG_FTL $CONFIG_MTD + dep_tristate 'NFTL (NAND Flash Translation Layer) support' CONFIG_NFTL $CONFIG_MTD + if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFTL" != "n" ]; then + bool 'Write support for NFTL (EXPERIMENTAL)' CONFIG_NFTL_RW + fi + dep_tristate 'Direct blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD + dep_tristate 'Direct chardevice access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD +fi + +endmenu diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile new file mode 100644 index 000000000..53a3906f8 --- /dev/null +++ b/drivers/mtd/Makefile @@ -0,0 +1,190 @@ +# $Id: Makefile,v 1.20 2000/07/04 08:58:10 dwmw2 Exp $ + +# Uncomment this to enable the DBG macro (see mtd.h) +#CFLAGS+= -DZDBG + +ifndef CONFIG_MTD +# We're being invoked outside a normal kernel build. Fake it +EXTRA_CFLAGS= -I$(shell pwd)/../include + +HWDRIVERS = slram.o docprobe.o doc1000.o nora.o physmap.o rpxlite.o vmax301.o octagon-5066.o pmc551.o mtdram.o +USERDRIVERS = ftl.o nftl.o mtdblock.o mtdchar.o +MIX_OBJS = mtdcore.o mapped.o doc2000.o doc2001.o cfi_probe.o cfi_cmdset_0001.o +MI_OBJS = $(HWDRIVERS) $(USERDRIVERS) +CFLAGS_nftl.o := -DCONFIG_NFTL_RW +else + +O_TARGET := mtdlink.o +SUB_DIRS := +ALL_SUB_DIRS := +MOD_LIST_NAME := MTD_MODULES + + +ifeq ($(CONFIG_MTD),y) + OX_OBJS += mtdcore.o mapped.o +else + ifeq ($(CONFIG_MTD),m) + MX_OBJS += mtdcore.o mapped.o + endif +endif + +ifeq ($(CONFIG_MTD_NORA),y) + O_OBJS += nora.o +else + ifeq ($(CONFIG_MTD_NORA),m) + M_OBJS += nora.o + endif +endif + +ifeq ($(CONFIG_MTD_RPXLITE),y) + O_OBJS += rpxlite.o +else + ifeq ($(CONFIG_MTD_RPXLITE),m) + M_OBJS += rpxlite.o + endif +endif + +ifeq ($(CONFIG_MTD_PHYSMAP),y) + O_OBJS += physmap.o +else + ifeq ($(CONFIG_MTD_PHYSMAP),m) + M_OBJS += physmap.o + endif +endif + +ifeq ($(CONFIG_MTD_CFI),y) + OX_OBJS += cfi_probe.o +else + ifeq ($(CONFIG_MTD_CFI),m) + MX_OBJS += cfi_probe.o + endif +endif + +ifeq ($(CONFIG_MTD_CFI_INTELEXT),y) + OX_OBJS += cfi_cmdset_0001.o +else + ifeq ($(CONFIG_MTD_CFI_INTELEXT),m) + MX_OBJS += cfi_cmdset_0001.o + endif +endif + +ifeq ($(CONFIG_MTD_DOC1000),y) + O_OBJS += doc1000.o +else + ifeq ($(CONFIG_MTD_DOC1000),m) + M_OBJS += doc1000.o + endif +endif + +ifeq ($(CONFIG_MTD_DOC2000),y) + OX_OBJS += doc2000.o +else + ifeq ($(CONFIG_MTD_DOC2000),m) + MX_OBJS += doc2000.o + endif +endif + +ifeq ($(CONFIG_MTD_DOC2001),y) + OX_OBJS += doc2001.o +else + ifeq ($(CONFIG_MTD_DOC2001),m) + MX_OBJS += doc2001.o + endif +endif + +ifeq ($(CONFIG_MTD_DOCPROBE),y) + O_OBJS += docprobe.o +else + ifeq ($(CONFIG_MTD_DOCPROBE),m) + M_OBJS += docprobe.o + endif +endif + +ifeq ($(CONFIG_MTD_SLRAM),y) + O_OBJS += slram.o +else + ifeq ($(CONFIG_MTD_SLRAM),m) + M_OBJS += slram.o + endif +endif + +ifeq ($(CONFIG_MTD_OCTAGON),y) + O_OBJS += octagon-5066.o +else + ifeq ($(CONFIG_MTD_OCTAGON),m) + M_OBJS += octagon-5066.o + endif +endif + +ifeq ($(CONFIG_MTD_PMC551),y) + O_OBJS += pmc551.o +else + ifeq ($(CONFIG_MTD_PMC551),m) + M_OBJS += pmc551.o + endif +endif + +ifeq ($(CONFIG_MTD_PMC551_BUGFIX),y) + CFLAGS_pmc551.o += -DPMC551_DRAM_BUG +endif + +ifeq ($(CONFIG_MTD_VMAX),y) + O_OBJS += vmax301.o +else + ifeq ($(CONFIG_MTD_VMAX),m) + M_OBJS += vmax301.o + endif +endif + +ifeq ($(CONFIG_MTD_MIXMEM),y) + O_OBJS += mixmem.o +else + ifeq ($(CONFIG_MTD_MIXMEM),m) + M_OBJS += mixmem.o + endif +endif + +ifeq ($(CONFIG_MTD_MTDRAM),y) + O_OBJS += mtdram.o +else + ifeq ($(CONFIG_MTD_MTDRAM),m) + M_OBJS += mtdram.o + endif +endif + +ifeq ($(CONFIG_FTL),y) + O_OBJS += ftl.o +else + ifeq ($(CONFIG_FTL),m) + M_OBJS += ftl.o + endif +endif + +ifeq ($(CONFIG_NFTL),y) + O_OBJS += nftl.o +else + ifeq ($(CONFIG_NFTL),m) + M_OBJS += nftl.o + endif +endif + +ifeq ($(CONFIG_MTD_BLOCK),y) + O_OBJS += mtdblock.o +else + ifeq ($(CONFIG_MTD_BLOCK),m) + M_OBJS += mtdblock.o + endif +endif + + +ifeq ($(CONFIG_MTD_CHAR),y) + O_OBJS += mtdchar.o +else + ifeq ($(CONFIG_MTD_CHAR),m) + M_OBJS += mtdchar.o + endif +endif + +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/cfi_cmdset_0001.c b/drivers/mtd/cfi_cmdset_0001.c new file mode 100644 index 000000000..713d8ab05 --- /dev/null +++ b/drivers/mtd/cfi_cmdset_0001.c @@ -0,0 +1,871 @@ +/* + * Common Flash Interface support: + * Intel Extended Vendor Command Set (ID 0x0001) + * + * (C) 2000 Red Hat. GPL'd + * + * $Id: cfi_cmdset_0001.c,v 1.20 2000/07/04 07:36:43 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/io.h> +#include <asm/byteorder.h> + +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/mtd/map.h> +#include <linux/mtd/cfi.h> + +#if LINUX_VERSION_CODE < 0x20300 +#define set_current_state(x) current->state = (x); +#endif +static int cfi_intelext_read_1_by_16 (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_1_by_16(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_intelext_erase_1_by_16 (struct mtd_info *, struct erase_info *); +static void cfi_intelext_sync (struct mtd_info *); +static int cfi_intelext_suspend (struct mtd_info *); +static void cfi_intelext_resume (struct mtd_info *); + +static void cfi_intelext_destroy(struct mtd_info *); + +void cfi_cmdset_0001(struct map_info *, int, unsigned long); +EXPORT_SYMBOL(cfi_cmdset_0001); + +struct mtd_info *cfi_intelext_setup (struct map_info *); + +void cfi_cmdset_0001(struct map_info *map, int primary, unsigned long base) +{ + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct cfi_pri_intelext *extp; + + __u16 adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; + + printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); + + if (!adr) + return; + + /* Switch it into Query Mode */ + switch(map->buswidth) { + case 1: + map->write8(map, 0x98, 0x55); + break; + case 2: + map->write16(map, 0x9898, 0xaa); + break; + case 4: + map->write32(map, 0x98989898, 0x154); + break; + } + + extp = kmalloc(sizeof(*extp), GFP_KERNEL); + if (!extp) { + printk("Failed to allocate memory\n"); + return; + } + + /* Read in the Extended Query Table */ + for (i=0; i<sizeof(*extp); i++) { + ((unsigned char *)extp)[i] = + map->read8(map, (base+((adr+i)*map->buswidth))); + } + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { + printk(" Unknown IntelExt Extended Query version %c.%c.\n", + extp->MajorVersion, extp->MinorVersion); + kfree(extp); + return; + } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le32_to_cpu(extp->BlkStatusRegMask); + + + /* Tell the user about it in lots of lovely detail */ +#if 0 + printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); + printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); + printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); + printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); + printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); + printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); + printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); + printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); + printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); + printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); + for (i=9; i<32; i++) { + if (extp->FeatureSupport & (1<<i)) + printk(" - Unknown Bit %X: supported\n", i); + } + + printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); + printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); + for (i=1; i<8; i++) { + if (extp->SuspendCmdSupport & (1<<i)) + printk(" - Unknown Bit %X: supported\n", i); + } + + printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); + printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); + printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); + for (i=2; i<16; i++) { + if (extp->BlkStatusRegMask & (1<<i)) + printk(" - Unknown Bit %X Active: yes\n",i); + } + + printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", + extp->VccOptimal >> 8, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", + extp->VppOptimal >> 8, extp->VppOptimal & 0xf); +#endif + /* OK. We like it. Take over the control of it. */ + + /* Switch it into Read Mode */ + switch(map->buswidth) { + case 1: + map->write8(map, 0xff, 0x55); + break; + case 2: + map->write16(map, 0xffff, 0xaa); + break; + case 4: + map->write32(map, 0xffffffff, 0x154); + break; + } + + + /* If there was an old setup function, decrease its use count */ + if (cfi->cmdset_setup) + put_module_symbol((unsigned long)cfi->cmdset_setup); + if (cfi->cmdset_priv) + kfree(cfi->cmdset_priv); + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 128; + cfi->chips[i].buffer_write_time = 128; + cfi->chips[i].erase_time = 1024; + } + + + cfi->cmdset_setup = cfi_intelext_setup; + cfi->cmdset_priv = extp; + MOD_INC_USE_COUNT; /* So the setup function is still there + * by the time it's called */ + + return; +} + +struct mtd_info *cfi_intelext_setup(struct map_info *map) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + printk("number of CFI chips: %d\n", cfi->numchips); + + if (!mtd) { + printk("Failed to allocate memory for MTD device\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + mtd->erasesize = 0x20000; /* FIXME */ + /* Also select the correct geometry setup too */ + mtd->size = (1 << cfi->cfiq.DevSize) * cfi->numchips; + mtd->erase = cfi_intelext_erase_1_by_16; + mtd->read = cfi_intelext_read_1_by_16; + mtd->write = cfi_intelext_write_1_by_16; + mtd->sync = cfi_intelext_sync; + mtd->suspend = cfi_intelext_suspend; + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv_destroy = cfi_intelext_destroy; + mtd->name = map->name; + return mtd; +} + +static inline int do_read_1_by_16_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + __u16 status; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. + * Later, we can actually think about interrupting it + * if it's in FL_ERASING or FL_WRITING state. + * Not just yet, though. + */ + switch (chip->state) { +#if 0 + case FL_ERASING: + case FL_WRITING: + /* Suspend the operation, set state to FL_xxx_SUSPENDED */ +#endif + + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + map->write16(map, cpu_to_le16(0x0070), adr); + chip->state = FL_STATUS; + + case FL_STATUS: + status = le16_to_cpu(map->read16(map, adr)); + + if (!(status & (1<<7))) { + static int z=0; + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in read"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet before read. looping\n"); + + udelay(1); + + goto retry; + } + break; + + default: + printk("Waiting for chip, status = %d\n", chip->state); + + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if(signal_pending(current)) + return -EINTR; + + + timeo = jiffies + HZ; + + goto retry; + } + + map->write16(map, cpu_to_le16(0x00ff), adr); + chip->state = FL_READY; + + map->copy_from(map, buf, adr, len); + + if (chip->state == FL_ERASE_SUSPENDED || + chip->state == FL_WRITE_SUSPENDED) { + printk("Who in hell suspended the pending operation? I didn't write that code yet!\n"); + /* Restart it and set the state accordingly */ + } + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return 0; +} + +static int cfi_intelext_read_1_by_16 (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + /* ofs: offset within the first chip that the first read should start */ + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<<cfi->chipshift) - ofs; + else + thislen = len; + + ret = do_read_1_by_16_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +static inline int do_write_1_by_16_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u16 datum) +{ + __u16 status; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + int z = 0; + adr += chip->start; + + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. + * Later, we can actually think about interrupting it + * if it's in FL_ERASING state. + * Not just yet, though. + */ + switch (chip->state) { + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + map->write16(map, cpu_to_le16(0x0070), adr); + chip->state = FL_STATUS; + timeo = jiffies + HZ; + + case FL_STATUS: + status = le16_to_cpu(map->read16(map, adr)); + + if (!(status & (1<<7))) { + + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in read"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet before write. looping\n"); + + udelay(1); + + goto retry; + } + break; + + default: + printk("Waiting for chip, status = %d\n", chip->state); + + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if(signal_pending(current)) + return -EINTR; + + timeo = jiffies + HZ; + + goto retry; + } + + map->write16(map, cpu_to_le16(0x0040), adr); + map->write16(map, datum, adr); + chip->state = FL_WRITING; + + timeo = jiffies + (HZ/2); + + spin_unlock_bh(chip->mutex); + udelay(chip->word_write_time); + spin_lock_bh(chip->mutex); + + z = 0; + while ( !( (status = le16_to_cpu(map->read16(map, adr))) & 0x80 ) ) { + + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if (signal_pending(current)) + return -EINTR; + + timeo = jiffies + (HZ / 2); /* FIXME */ + + spin_lock_bh(chip->mutex); + continue; + } + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in read"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet after write. looping\n"); + + udelay(1); + + spin_lock_bh(chip->mutex); + continue; + } + if (!z) { + chip->word_write_time--; + if (!chip->word_write_time) + chip->word_write_time++; + else + printk("decreasing word_write_time to %d µs\n", chip->word_write_time); + } + if (z > 100) { + chip->word_write_time++; + printk("increasing word_write_time to %d µs\n", chip->word_write_time); + } + + /* Done and happy. */ + chip->state = FL_STATUS; + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + // printk("write ret OK at %lx\n", adr); + return 0; +} + + +/* This version only uses the 'word write' instruction. We should update it + * to write using 'buffer write' if it's available + */ +static int cfi_intelext_write_1_by_16 (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs; + + *retlen = 0; + chipnum = to >> cfi->chipshift; + ofs = to - (chipnum << cfi->chipshift); + + /* If it's not word-aligned, do the first byte write */ + if (ofs & 1) { +#if defined(__LITTLE_ENDIAN) + ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], + ofs, 0xFF | (*buf << 8)); +#elif defined(__BIG_ENDIAN) + ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], + ofs, 0xFF00 | (*buf)); +#else +#error define a sensible endianness +#endif + if (ret) + return ret; + + ofs++; + buf++; + (*retlen)++; + len--; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + while(len > 1) { + ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], + ofs, *(__u16 *)buf); + if (ret) + return ret; + + ofs += 2; + buf += 2; + (*retlen) += 2; + len -= 2; + + if (ofs >> cfi->chipshift) { + chipnum ++; + ofs = 0; + if (chipnum == cfi->numchips) + return 0; + } + } + + if (len) { + /* Final byte to write */ +#if defined(__LITTLE_ENDIAN) + ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], + ofs, 0xFF00 | (*buf)); +#elif defined(__BIG_ENDIAN) + ret = do_write_1_by_16_oneword(map, &cfi->chips[chipnum], + ofs, 0xFF | (*buf << 8)); +#else +#error define a sensible endianness +#endif + if (ret) + return ret; + + (*retlen)++; + } + + return 0; +} + + +static inline int do_erase_1_by_16_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + __u16 status; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + retry: + spin_lock_bh(chip->mutex); + + /* Check that the chip's ready to talk to us. */ + switch (chip->state) { + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + case FL_READY: + map->write16(map, cpu_to_le16(0x0070), adr); + chip->state = FL_STATUS; + timeo = jiffies + HZ; + + case FL_STATUS: + status = le16_to_cpu(map->read16(map, adr)); + + if (!(status & (1<<7))) { + static int z=0; + /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { + spin_unlock_bh(chip->mutex); + printk("waiting for chip to be ready timed out in erase"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet before erase. looping\n"); + + udelay(1); + + goto retry; + } + break; + + default: + printk("Waiting for chip, status = %d\n", chip->state); + + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if(signal_pending(current)) + return -EINTR; + + timeo = jiffies + HZ; + + goto retry; + } + + map->write16(map, cpu_to_le16(0x0020), adr); + map->write16(map, cpu_to_le16(0x00D0), adr); + + chip->state = FL_ERASING; + + timeo = jiffies + (HZ*2); + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + while ( !( (status = le16_to_cpu(map->read16(map, adr))) & 0x80 ) ) { + static int z=0; + + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + printk("erase suspended. Sleeping\n"); + + schedule(); + remove_wait_queue(&chip->wq, &wait); + + if (signal_pending(current)) + return -EINTR; + + timeo = jiffies + (HZ*2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + printk("waiting for erase to complete timed out."); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + + z++; + if ( 0 && !(z % 100 )) + printk("chip not ready yet after erase. looping\n"); + + udelay(1); + + spin_lock_bh(chip->mutex); + continue; + } + + /* Done and happy. */ + chip->state = FL_STATUS; + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + //printk("erase ret OK\n"); + return 0; +} + +static int cfi_intelext_erase_1_by_16 (struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr, len; + int chipnum, ret = 0; + + if (instr->addr & (mtd->erasesize - 1)) + return -EINVAL; + + if (instr->len & (mtd->erasesize -1)) + return -EINVAL; + + if ((instr->len + instr->addr) > mtd->size) + return -EINVAL; + + chipnum = instr->addr >> cfi->chipshift; + adr = instr->addr - (chipnum << cfi->chipshift); + len = instr->len; + + while(len) { + ret = do_erase_1_by_16_oneblock(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += mtd->erasesize; + len -= mtd->erasesize; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + if (instr->callback) + instr->callback(instr); + + return 0; +} + + + +static void cfi_intelext_sync (struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && i<cfi->numchips; i++) { + chip = &cfi->chips[i]; + + retry: + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + spin_unlock_bh(chip->mutex); + break; + + default: + /* Not an idle state */ + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + schedule(); + + goto retry; + } + } + + /* Unlock the chips again */ + + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_SYNCING) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } +} + + +static int cfi_intelext_suspend(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + + for (i=0; !ret && i<cfi->numchips; i++) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ + spin_unlock_bh(chip->mutex); + break; + + default: + ret = -EAGAIN; + break; + } + } + + /* Unlock the chips again */ + + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } + + return ret; +} + +static void cfi_intelext_resume(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + + for (i=0; i<cfi->numchips; i++) { + + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + else + printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n"); + + spin_unlock_bh(chip->mutex); + } +} + +static void cfi_intelext_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + kfree(cfi->cmdset_priv); + kfree(cfi); +} + diff --git a/drivers/mtd/cfi_probe.c b/drivers/mtd/cfi_probe.c new file mode 100644 index 000000000..ccd33fb3c --- /dev/null +++ b/drivers/mtd/cfi_probe.c @@ -0,0 +1,501 @@ +/* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. + $Id: cfi_probe.c,v 1.12 2000/07/03 13:29:16 dwmw2 Exp $ +*/ + + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +#include <linux/mtd/map.h> +#include <linux/mtd/cfi.h> + + +struct mtd_info *cfi_probe(struct map_info *); +EXPORT_SYMBOL(cfi_probe); + +static void print_cfi_ident(struct cfi_ident *); +static void check_cmd_set(struct map_info *, int, unsigned long); +static struct cfi_private *cfi_cfi_probe(struct map_info *); + +struct mtd_info *cfi_probe(struct map_info *map) +{ + struct mtd_info *mtd = NULL; + struct cfi_private *cfi; + /* First probe the map to see if we have CFI stuff there. */ + cfi = cfi_cfi_probe(map); + + if (!cfi) + return NULL; + + map->fldrv_priv = cfi; + + /* OK we liked it. Now find a driver for the command set it talks */ + + check_cmd_set(map, 1, cfi->chips[0].start); /* First the primary cmdset */ + check_cmd_set(map, 0, cfi->chips[0].start); /* Then the secondary */ + + /* check_cmd_set() will have used get_module_symbol to increase + the use count of the module which provides the command set + driver. If we're quitting, we have to decrease it again. + */ + + if(cfi->cmdset_setup) { + mtd = cfi->cmdset_setup(map); + + if (mtd) + return mtd; + put_module_symbol((unsigned long)cfi->cmdset_setup); + } + printk("No supported Vendor Command Set found\n"); + + kfree(cfi); + map->fldrv_priv = NULL; + return NULL; + +} + +static int cfi_probe_new_chip(struct map_info *map, unsigned long base, + struct flchip *chips, struct cfi_private *cfi) +{ + switch (map->buswidth) { + + case 1: { + unsigned char tmp = map->read8(map, base + 0x55); + + /* If there's a device there, put it in Query Mode */ + map->write8(map, 0x98, base+0x55); + + if (map->read8(map,base+0x10) == 'Q' && + map->read8(map,base+0x11) == 'R' && + map->read8(map,base+0x12) == 'Y') { + printk("%s: Found a CFI device at 0x%lx in 8 bit mode\n", map->name, base); + if (chips) { + /* Check previous chips for aliases */ + printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); + /* Put it back into Read Mode */ + map->write8(map, 0x98, base+0x55); + } + return 1; + } else { + if (map->read8(map, base + 0x55) == 0x98) { + /* It looks like RAM. Put back the stuff we overwrote */ + map->write8(map, tmp, base+0x55); + } + return 0; + } + } + + case 2: { + __u16 tmp = map->read16(map, base + 0xaa); + + /* If there's a device there, put it into Query Mode */ + map->write16(map, 0x9898, base+0xAA); + + if (map->read16(map, base+0x20) == cpu_to_le16(0x0051) && + map->read16(map, base+0x22) == cpu_to_le16(0x0052) && + map->read16(map, base+0x24) == cpu_to_le16(0x0059)) { + printk("%s: Found a CFI device at 0x%lx in 16 bit mode\n", map->name, base); + if (chips) { + /* Check previous chips for aliases */ + int i; + + for (i=0; i < cfi->numchips; i++) { + /* This chip should be in read mode if it's one + we've already touched. */ + if (map->read16(map, chips[i].start+0x20) == cpu_to_le16(0x0051) && + map->read16(map, chips[i].start+0x22) == cpu_to_le16(0x0052) && + map->read16(map, chips[i].start+0x24) == cpu_to_le16(0x0059)){ + /* Either the old chip has got 'Q''R''Y' in a most + unfortunate place, or it's an alias of the new + chip. Double-check that it's in read mode, and check. */ + map->write16(map, 0xffff, chips[i].start+0x20); + if (map->read16(map, chips[i].start+0x20) == cpu_to_le16(0x0051) && + map->read16(map, chips[i].start+0x22) == cpu_to_le16(0x0052) && + map->read16(map, chips[i].start+0x24) == cpu_to_le16(0x0059)) { + /* Yes it's got QRY for data. Most unfortunate. + Stick the old one in read mode too. */ + map->write16(map, 0xffff, base); + if (map->read16(map, base+0x20) == cpu_to_le16(0x0051) && + map->read16(map, base+0x22) == cpu_to_le16(0x0052) && + map->read16(map, base+0x24) == cpu_to_le16(0x0059)) { + /* OK, so has the new one. Assume it's an alias */ + printk("T'was probably an alias for the chip at 0x%lx\n", chips[i].start); + return 1; + } /* else no, they're different, fall through. */ + } else { + /* No the old one hasn't got QRY for data. Therefore, + it's an alias of the new one. */ + map->write16(map, 0xffff, base+0xaa); + /* Just to be paranoid. */ + map->write16(map, 0xffff, chips[i].start+0xaa); + printk("T'was an alias for the chip at 0x%lx\n", chips[i].start); + return 1; + } + } + /* No, the old one didn't look like it's in query mode. Next. */ + } + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ + if (cfi->numchips == MAX_CFI_CHIPS) { + printk("%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); + /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ + return 1; + } + printk("Not an alias. Adding\n"); + chips[cfi->numchips].start = base; + chips[cfi->numchips].state = FL_READY; + chips[cfi->numchips].mutex = &chips[cfi->numchips]._spinlock; + cfi->numchips++; + + /* Put it back into Read Mode */ + map->write16(map, 0xffff, base+0xaa); + } + + return 1; + } + else if (map->read16(map, base+0x20) == 0x5151 && + map->read16(map, base+0x22) == 0x5252 && + map->read16(map, base+0x24) == 0x5959) { + printk("%s: Found a coupled pair of CFI devices at %lx in 8 bit mode\n", + map->name, base); + if (chips) { + /* Check previous chips for aliases */ + printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); + + /* Put it back into Read Mode */ + map->write16(map, 0xffff, base+0xaa); + } + + return 2; + } else { + if (map->read16(map, base+0xaa) == 0x9898) { + /* It looks like RAM. Put back the stuff we overwrote */ + map->write16(map, tmp, base+0xaa); + } + return 0; + } + } + + + case 4: { + __u32 tmp = map->read32(map, base+0x154); + + /* If there's a device there, put it into Query Mode */ + map->write32(map, 0x98989898, base+0x154); + + if (map->read32(map, base+0x40) == cpu_to_le32(0x00000051) && + map->read32(map, base+0x44) == cpu_to_le32(0x00000052) && + map->read32(map, base+0x48) == cpu_to_le32(0x00000059)) { + /* This isn't actually in the CFI spec - only x8 and x16 are. */ + printk("%s: Found a CFI device at %lx in 32 bit mode\n", map->name, base); + if (chips) { + /* Check previous chips for aliases */ + printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); + + /* Put it back into read mode */ + map->write32(map, 0xffffffff, base+0x154); + } + return 1; + } + else if (map->read32(map, base+0x40) == cpu_to_le32(0x00510051) && + map->read32(map, base+0x44) == cpu_to_le32(0x00520052) && + map->read32(map, base+0x48) == cpu_to_le32(0x00590059)) { + printk("%s: Found a coupled pair of CFI devices at location %lx in 16 bit mode\n", map->name, base); + if (chips) { + /* Check previous chips for aliases */ + printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); + + /* Put it back into read mode */ + map->write32(map, 0xffffffff, base+0x154); + } + return 2; + } + else if (map->read32(map, base+0x40) == 0x51515151 && + map->read32(map, base+0x44) == 0x52525252 && + map->read32(map, base+0x48) == 0x59595959) { + printk("%s: Found four side-by-side CFI devices at location %lx in 8 bit mode\n", map->name, base); + if (chips) { + /* Check previous chips for aliases */ + printk("FIXME: Do alias check at line %d of cfi_probe.c\n", __LINE__); + + /* Put it back into read mode */ + map->write32(map, 0xffffffff, base+0x154); + } + return 4; + } else { + if (map->read32(map, base+0x154) == 0x98989898) { + /* It looks like RAM. Put back the stuff we overwrote */ + map->write32(map, tmp, base+0x154); + } + return 0; + } + } + default: + printk(KERN_WARNING "cfi_cfi_probe called with strange buswidth %d\n", map->buswidth); + return 0; + } +} + +static struct cfi_private *cfi_cfi_probe(struct map_info *map) +{ + unsigned long base=0; + struct cfi_private cfi; + struct cfi_private *retcfi; + struct flchip chip[MAX_CFI_CHIPS]; + int i; + + memset(&cfi, 0, sizeof(cfi)); + + /* The first invocation (with chips == NULL) leaves the device in Query Mode */ + cfi.interleave = cfi_probe_new_chip(map, 0, NULL, NULL); + + if (!cfi.interleave) { + printk("%s: Found no CFI device at location zero\n", map->name); + /* Doesn't appear to be CFI-compliant at all */ + return NULL; + } + + /* Read the Basic Query Structure from the device */ + + for (i=0; i<sizeof(struct cfi_ident); i++) { + ((unsigned char *)&cfi.cfiq)[i] = map->read8(map,base + ((0x10 + i)*map->buswidth)); + } + + /* Do any necessary byteswapping */ + cfi.cfiq.P_ID = le16_to_cpu(cfi.cfiq.P_ID); + cfi.cfiq.P_ADR = le16_to_cpu(cfi.cfiq.P_ADR); + cfi.cfiq.A_ID = le16_to_cpu(cfi.cfiq.A_ID); + cfi.cfiq.A_ADR = le16_to_cpu(cfi.cfiq.A_ADR); + cfi.cfiq.InterfaceDesc = le16_to_cpu(cfi.cfiq.InterfaceDesc); + cfi.cfiq.MaxBufWriteSize = le16_to_cpu(cfi.cfiq.MaxBufWriteSize); + +#if 1 + /* Dump the information therein */ + print_cfi_ident(&cfi.cfiq); + + for (i=0; i<cfi.cfiq.NumEraseRegions; i++) { + __u16 EraseRegionInfoNum = (map->read8(map,base + ((0x2d + (4*i))*map->buswidth))) + + (((map->read8(map,(0x2e + (4*i))*map->buswidth)) << 8)); + __u16 EraseRegionInfoSize = (map->read8(map, base + ((0x2f + (4*i))*map->buswidth))) + + (map->read8(map, base + ((0x30 + (4*i))*map->buswidth)) << 8); + + printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks\n", + i, EraseRegionInfoSize * 256, EraseRegionInfoNum+1); + } + + printk("\n"); +#endif + + /* Switch the chip back into Read Mode, to make the alias detection work */ + switch(map->buswidth) { + case 1: + map->write8(map, 0xff, 0x55); + break; + case 2: + map->write16(map, 0xffff, 0xaa); + break; + case 4: + map->write32(map, 0xffffffff, 0x154); + break; + } + + /* OK. We've worked out what it is and we're happy with it. Now see if there are others */ + + chip[0].start = 0; + chip[0].state = FL_READY; + chip[0].mutex = &chip[0]._spinlock; + + cfi.chipshift = cfi.cfiq.DevSize; + cfi.numchips = 1; + + if (!cfi.chipshift) { + printk("cfi.chipsize is zero. This is bad. cfi.cfiq.DevSize is %d\n", cfi.cfiq.DevSize); + return NULL; + } + + for (base = (1<<cfi.chipshift); base < map->size; base += (1<<cfi.chipshift)) + cfi_probe_new_chip(map, base, &chip[0], &cfi); + + retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL); + + if (!retcfi) + return NULL; + + memcpy(retcfi, &cfi, sizeof(cfi)); + memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); + for (i=0; i< retcfi->numchips; i++) { + init_waitqueue_head(&retcfi->chips[i].wq); + spin_lock_init(&retcfi->chips[i]._spinlock); + } + return retcfi; +} + +static char *vendorname(__u16 vendor) +{ + switch (vendor) { + case P_ID_NONE: + return "None"; + + case P_ID_INTEL_EXT: + return "Intel/Sharp Extended"; + + case P_ID_AMD_STD: + return "AMD/Fujitsu Standard"; + + case P_ID_INTEL_STD: + return "Intel/Sharp Standard"; + + case P_ID_AMD_EXT: + return "AMD/Fujitsu Extended"; + + case P_ID_MITSUBISHI_STD: + return "Mitsubishi Standard"; + + case P_ID_MITSUBISHI_EXT: + return "Mitsubishi Extended"; + + case P_ID_RESERVED: + return "Not Allowed / Reserved for Future Use"; + + default: + return "Unknown"; + } +} + + +static void print_cfi_ident(struct cfi_ident *cfip) +{ + if (cfip->qry[0] != 'Q' || cfip->qry[1] != 'R' || cfip->qry[2] != 'Y') { + printk("Invalid CFI ident structure.\n"); + return; + } + + printk("Primary Vendor Command Set: %4.4X (%s)\n", cfip->P_ID, vendorname(cfip->P_ID)); + if (cfip->P_ADR) + printk("Primary Algorithm Table at %4.4X\n", cfip->P_ADR); + else + printk("No Primary Algorithm Table\n"); + + printk("Alternative Vendor Command Set: %4.4X (%s)\n", cfip->A_ID, vendorname(cfip->A_ID)); + if (cfip->A_ADR) + printk("Alternate Algorithm Table at %4.4X\n", cfip->A_ADR); + else + printk("No Alternate Algorithm Table\n"); + + + printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); + printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + if (cfip->VppMin) { + printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); + printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + } + else + printk("No Vpp line\n"); + + printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp); + printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp)); + + if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) { + printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp); + printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp)); + } + else + printk("Full buffer write not supported\n"); + + printk("Typical block erase timeout: %d µs\n", 1<<cfip->BlockEraseTimeoutTyp); + printk("Maximum block erase timeout: %d µs\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp)); + if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) { + printk("Typical chip erase timeout: %d µs\n", 1<<cfip->ChipEraseTimeoutTyp); + printk("Maximum chip erase timeout: %d µs\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->ChipEraseTimeoutTyp)); + } + else + printk("Chip erase not supported\n"); + + printk("Device size: 0x%X bytes (%d Mb)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20)); + printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc); + switch(cfip->InterfaceDesc) { + case 0: + printk(" - x8-only asynchronous interface\n"); + break; + + case 1: + printk(" - x16-only asynchronous interface\n"); + break; + + case 2: + printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n"); + break; + + case 3: + printk(" - x32-only asynchronous interface\n"); + break; + + case 65535: + printk(" - Not Allowed / Reserved\n"); + break; + + default: + printk(" - Unknown\n"); + break; + } + + printk("Max. bytes in buffer write: 0x%x\n", 1<< cfip->MaxBufWriteSize); + printk("Number of Erase Block Regions: %d\n", cfip->NumEraseRegions); + +} + +static void check_cmd_set(struct map_info *map, int primary, unsigned long base) +{ + __u16 adr; + struct cfi_private *cfi = map->fldrv_priv; + __u16 type = primary?cfi->cfiq.P_ID:cfi->cfiq.A_ID; + char probename[32]; + void (*probe_function)(struct map_info *, int, unsigned long); + + if (type == P_ID_NONE || type == P_ID_RESERVED) + return; + + sprintf(probename, "cfi_cmdset_%4.4X", type); + + probe_function = (void *)get_module_symbol(NULL, probename); + if (!probe_function) { + request_module(probename); + + probe_function = (void *)get_module_symbol(NULL, probename); + } + + if (probe_function) { + (*probe_function)(map, primary, base); + put_module_symbol((unsigned long)probe_function); + return; + } + + /* This was a command set we don't know about. Print only the basic info */ + adr = primary?cfi->cfiq.P_ADR:cfi->cfiq.A_ADR; + + if (!adr) { + printk(" No Extended Query Table\n"); + } + else if (map->read8(map,base+(adr*map->buswidth)) != (primary?'P':'A') || + map->read8(map,base+((adr+1)*map->buswidth)) != (primary?'R':'L') || + map->read8(map,base+((adr+2)*map->buswidth)) != (primary?'I':'T')) { + printk ("Invalid Extended Query Table at %4.4X: %2.2X %2.2X %2.2X\n", + adr, + map->read8(map,base+(adr*map->buswidth)), + map->read8(map,base+((adr+1)*map->buswidth)), + map->read8(map,base+((adr+2)*map->buswidth))); + } + else { + printk(" Extended Query Table version %c.%c\n", + map->read8(map,base+((adr+3)*map->buswidth)), + map->read8(map,base+((adr+4)*map->buswidth))); + } +} diff --git a/drivers/mtd/doc1000.c b/drivers/mtd/doc1000.c new file mode 100644 index 000000000..3b288b129 --- /dev/null +++ b/drivers/mtd/doc1000.c @@ -0,0 +1,601 @@ +/*====================================================================== + + $Id: doc1000.c,v 1.8 2000/07/03 10:01:38 dwmw2 Exp $ + + A general driver for accessing PCMCIA card memory via Bulk + Memory Services. + + This driver provides the equivalent of /dev/mem for a PCMCIA + card's attribute and common memory. It includes character + and block devices. + + Written by David Hinds, dhinds@allegro.stanford.edu + +======================================================================*/ + + +#include <linux/module.h> +#include <asm/uaccess.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <stdarg.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/iflash.h> + +/* Parameters that can be set with 'insmod' */ + +static u_long base = 0xe0000; +static int erase_timeout = 10*HZ; /* in ticks */ +static int retry_limit = 4; /* write retries */ +static u_long max_tries = 4096; /* status polling */ + +MODULE_PARM(base,"l"); +MODULE_PARM(erase_timeout, "i"); +MODULE_PARM(retry_limit, "i"); +MODULE_PARM(max_tries, "i"); + +#define WINDOW_SIZE 0x2000 +#define WINDOW_MASK (WINDOW_SIZE - 1) +#define PAGEREG_LO (WINDOW_SIZE) +#define PAGEREG_HI (WINDOW_SIZE + 2) + +static struct mtd_info *mymtd; +static struct timer_list flashcard_timer; + +#define MAX_CELLS 32 +#define MAX_FLASH_DEVICES 8 + +/* A flash region is composed of one or more "cells", where we allow + simultaneous erases if they are in different cells */ + + + +struct mypriv { + u_char *baseaddr; + u_short curpage; + u_char locked; + u_short numdevices; + u_char interleave; + struct erase_info *cur_erases; + wait_queue_head_t wq; + u_char devstat[MAX_FLASH_DEVICES]; + u_long devshift; +}; + + +static void flashcard_periodic(u_long data); +static int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr); +static int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); +static void flashcard_sync (struct mtd_info *mtd); + +static inline void resume_erase(volatile u_char *addr); +static inline int suspend_erase(volatile u_char *addr); +static inline int byte_write (volatile u_char *addr, u_char byte); +static inline int word_write (volatile u_char *addr, __u16 word); +static inline int check_write(volatile u_char *addr); +static inline void block_erase (volatile u_char *addr); +static inline int check_erase(volatile u_char *addr); + +#ifdef __SMP__ +#warning This is definitely not SMP safe. Lock the paging mechanism. +#endif + +static u_char *pagein(struct mtd_info *mtd, u_long addr) +{ + struct mypriv *priv=mtd->priv; + u_short page = addr >> 13; + + priv->baseaddr[PAGEREG_LO] = page & 0xff; + priv->baseaddr[PAGEREG_HI] = page >> 8; + priv->curpage = page; + + return &priv->baseaddr[addr & WINDOW_MASK]; +} + + +void flashcard_sync (struct mtd_info *mtd) +{ + struct mypriv *priv=mtd->priv; + + flashcard_periodic((u_long) mtd); + printk("sync..."); + if (priv->cur_erases) + interruptible_sleep_on(&priv->wq); + printk("Done.\n"); +} + +int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + u_char *pageaddr; + struct mypriv *priv=mtd->priv; + struct erase_info **tmp=&priv->cur_erases; + + if (instr->len != mtd->erasesize) + return -EINVAL; + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + pageaddr=pagein(mtd,instr->addr); + instr->mtd = mtd; + instr->dev = instr->addr >> priv->devshift; + instr->cell = (instr->addr - (instr->dev << priv->devshift)) / mtd->erasesize; + instr->next = NULL; + instr->state = MTD_ERASE_PENDING; + + while (*tmp) + { + tmp = &((*tmp) -> next); + } + + *tmp = instr; + flashcard_periodic((u_long)mtd); + return 0; +} + + +int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + u_char *pageaddr=pagein(mtd,from); + struct mypriv *priv=mtd->priv; + u_char device = from >> priv->devshift; + u_char cell = (int) (from - (device << priv->devshift)) / mtd->erasesize; + int ret = 0, timeron = 0; + + if ((from & WINDOW_MASK) + len <= WINDOW_SIZE) + *retlen = len; + else + *retlen = WINDOW_SIZE - (from & WINDOW_MASK); + + if (priv->devstat[device]) + { + + /* There is an erase in progress or pending for this device. Stop it */ + timeron = del_timer(&flashcard_timer); + + if (priv->cur_erases && priv->cur_erases->cell == cell) + + { + /* The erase is on the current cell. Just return all 0xff */ + add_timer(&flashcard_timer); + + + printk("Cell %d currently erasing. Setting to all 0xff\n",cell); + memset(buf, 0xff, *retlen); + return 0; + } + if (priv->devstat[device] == MTD_ERASING) + { + ret = suspend_erase(pageaddr); + priv->devstat[device] = MTD_ERASE_SUSPEND; + + if (ret) + { + printk("flashcard: failed to suspend erase\n"); + add_timer (&flashcard_timer); + return ret; + } + } + + } + + writew(IF_READ_ARRAY, (u_long)pageaddr & ~1); + + ret = 0; + memcpy (buf, pageaddr, *retlen); + + writew(IF_READ_CSR, (u_long)pageaddr & ~1); + + + if (priv->devstat[device] & MTD_ERASE_SUSPEND) + { + resume_erase(pageaddr); + priv->devstat[device]=MTD_ERASING; + } + + + if (timeron) add_timer (&flashcard_timer); + + return ret; +} + + +int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + u_char *endaddr, *startaddr; + register u_char *pageaddr; + u_char device = to >> priv->devshift; +/* jiffies_t oldj=jiffies;*/ + int ret; + + while (priv->devstat[device]) + { + flashcard_sync(mtd); + } + + if ((to & WINDOW_MASK) + len <= WINDOW_SIZE) + *retlen = len; + else + *retlen = WINDOW_SIZE - (to & WINDOW_MASK); + + pageaddr = pagein(mtd, to); + startaddr = (u_char *)((u_long) pageaddr & ~1); + endaddr = pageaddr+(*retlen); + + + + /* Set up to read */ + writew(IF_READ_CSR, startaddr); + + /* Make sure it's aligned by reading the first byte if necessary */ + if (to & 1) + { + /* Unaligned access */ + + u_char cbuf; + + cbuf = *buf; + + if (!((u_long)pageaddr & 0xf)) + schedule(); + + ret = byte_write(pageaddr, cbuf); + if (ret) return ret; + + pageaddr++; buf++; + } + + + for ( ; pageaddr + 1 < endaddr; buf += 2, pageaddr += 2) + { + /* if ((u_long)pageaddr & 0xf) schedule();*/ + + ret = word_write(pageaddr, *(__u16 *)buf); + if (ret) + return ret; + } + + if (pageaddr != endaddr) + { + /* One more byte to write at the end. */ + u_char cbuf; + + cbuf = *buf; + + ret = byte_write(pageaddr, cbuf); + + if (ret) return ret; + } + + return check_write(startaddr); +/* printk("Time taken in flashcard_write: %lx jiffies\n",jiffies - oldj);*/ +} + + + + +/*====================================================================*/ + +static inline int byte_write (volatile u_char *addr, u_char byte) +{ + register u_char status; + register u_short i; + + for (i = 0; i < max_tries; i++) + { + status = readb(addr); + if (status & CSR_WR_READY) + { + writeb(IF_WRITE & 0xff, addr); + writeb(byte, addr); + return 0; + } + } + + printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status); + return -EIO; +} + +static inline int word_write (volatile u_char *addr, __u16 word) +{ + register u_short status = 0; + register u_short i; + + for (i = 0; i < max_tries; i++) + { + status = readw(addr); + if ((status & CSR_WR_READY) == CSR_WR_READY) + { + writew(IF_WRITE, addr); + writew(word, addr); + return 0; + } + } + + printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status); + return -EIO; +} + +static inline void block_erase (volatile u_char *addr) +{ + writew(IF_BLOCK_ERASE, addr); + writew(IF_CONFIRM, addr); +} + + +static inline int check_erase(volatile u_char *addr) +{ + __u16 status; + +/* writew(IF_READ_CSR, addr);*/ + status = readw(addr); + + + if ((status & CSR_WR_READY) != CSR_WR_READY) + return -EBUSY; + + if (status & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR)) + { + printk(KERN_NOTICE "flashcard: erase failed, status 0x%x\n", + status); + return -EIO; + } + + return 0; +} + +static inline int suspend_erase(volatile u_char *addr) +{ + __u16 status = 0; + u_long i; + + writew(IF_ERASE_SUSPEND, addr); + writew(IF_READ_CSR, addr); + + for (i = 0; i < max_tries; i++) + { + status = readw(addr); + if ((status & CSR_WR_READY) == CSR_WR_READY) break; + } + if (i == max_tries) + { + printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status); + return -EIO; + } + + return 0; +} + +static inline void resume_erase(volatile u_char *addr) +{ + __u16 status; + + writew(IF_READ_CSR, addr); + status = readw(addr); + + /* Only give resume signal if the erase is really suspended */ + if (status & CSR_ERA_SUSPEND) + writew(IF_CONFIRM, addr); +} + +static inline void reset_block(volatile u_char *addr) +{ + u_short i; + __u16 status; + + writew(IF_CLEAR_CSR, addr); + + for (i = 0; i < 100; i++) { + writew(IF_READ_CSR, addr); + status = readw(addr); + if (status != 0xffff) break; + udelay(1000); + } + + writew(IF_READ_CSR, addr); +} + +static inline int check_write(volatile u_char *addr) +{ + u_short status = 0, i; + + writew(IF_READ_CSR, addr); + + for (i=0; i < max_tries; i++) + { + status = readw(addr); + if (status & (CSR_WR_ERR | CSR_VPP_LOW)) + { + printk(KERN_NOTICE "flashcard: write failure at %p, status 0x%x\n", addr, status); + reset_block(addr); + return -EIO; + } + if ((status & CSR_WR_READY) == CSR_WR_READY) + return 0; + } + printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status); + return -EIO; +} + + +/*====================================================================*/ + + + +static void flashcard_periodic(unsigned long data) +{ + register struct mtd_info *mtd = (struct mtd_info *)data; + register struct mypriv *priv = mtd->priv; + struct erase_info *erase = priv->cur_erases; + u_char *pageaddr; + + del_timer (&flashcard_timer); + + if (!erase) + return; + + pageaddr = pagein(mtd, erase->addr); + + if (erase->state == MTD_ERASE_PENDING) + { + block_erase(pageaddr); + priv->devstat[erase->dev] = erase->state = MTD_ERASING; + erase->time = jiffies; + erase->retries = 0; + } + else if (erase->state == MTD_ERASING) + { + /* It's trying to erase. Check whether it's finished */ + + int ret = check_erase(pageaddr); + + if (!ret) + { + /* It's finished OK */ + priv->devstat[erase->dev] = 0; + priv->cur_erases = erase->next; + erase->state = MTD_ERASE_DONE; + if (erase->callback) + (*(erase->callback))(erase); + else + kfree(erase); + } + else if (ret == -EIO) + { + if (++erase->retries > retry_limit) + { + printk("Failed too many times. Giving up\n"); + priv->cur_erases = erase->next; + priv->devstat[erase->dev] = 0; + erase->state = MTD_ERASE_FAILED; + if (erase->callback) + (*(erase->callback))(erase); + else + kfree(erase); + } + else + priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING; + } + else if (erase->time + erase_timeout < jiffies) + { + printk("Flash erase timed out. The world is broken.\n"); + + /* Just ignore and hope it goes away. For a while, read ops will give the CSR + and writes won't work. */ + + priv->cur_erases = erase->next; + priv->devstat[erase->dev] = 0; + erase->state = MTD_ERASE_FAILED; + if (erase->callback) + (*(erase->callback))(erase); + else + kfree(erase); + } + } + + if (priv->cur_erases) + { + flashcard_timer.expires = jiffies + HZ; + add_timer (&flashcard_timer); + } + else + wake_up_interruptible(&priv->wq); + +} + +#if defined (MODULE) && LINUX_VERSION_CODE < 0x20300 +#define init_doc1000 init_module +#define cleanup_doc1000 cleanup_module +#endif + +int __init init_doc1000(void) +{ + struct mypriv *priv; + + if (!base) + { + printk(KERN_NOTICE "flashcard: No start address for memory device.\n"); + return -EINVAL; + } + + mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + + if (!mymtd) + { + printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device.\n"); + return -ENOMEM; + } + + memset(mymtd,0,sizeof(struct mtd_info)); + + mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL); + if (!mymtd->priv) + { + kfree(mymtd); + printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device's private data.\n"); + return -ENOMEM; + } + + + + + priv=mymtd->priv; + init_waitqueue_head(&priv->wq); + + memset (priv,0,sizeof(struct mypriv)); + + priv->baseaddr = phys_to_virt(base); + priv->numdevices = 4; + + mymtd->name = "M-Systems DiskOnChip 1000"; + + mymtd->size = 0x100000; + mymtd->flags = MTD_CLEAR_BITS | MTD_ERASEABLE; + mymtd->erase = flashcard_erase; + mymtd->point = NULL; + mymtd->unpoint = NULL; + mymtd->read = flashcard_read; + mymtd->write = flashcard_write; + + mymtd->sync = flashcard_sync; + mymtd->erasesize = 0x10000; + // mymtd->interleave = 2; + priv->devshift = 24; + mymtd->type = MTD_NORFLASH; + + if (add_mtd_device(mymtd)) + { + printk(KERN_NOTICE "MTD device registration failed!\n"); + kfree(mymtd->priv); + kfree(mymtd); + return -EAGAIN; + } + + init_timer(&flashcard_timer); + flashcard_timer.function = flashcard_periodic; + flashcard_timer.data = (u_long)mymtd; + return 0; +} + +static void __init cleanup_doc1000(void) +{ + kfree (mymtd->priv); + del_mtd_device(mymtd); + kfree(mymtd); +} + diff --git a/drivers/mtd/doc2000.c b/drivers/mtd/doc2000.c new file mode 100644 index 000000000..8385648d1 --- /dev/null +++ b/drivers/mtd/doc2000.c @@ -0,0 +1,837 @@ + +/* Linux driver for Disk-On-Chip 2000 */ +/* (c) 1999 Machine Vision Holdings, Inc. */ +/* Author: David Woodhouse <dwmw2@mvhi.com> */ +/* $Id: doc2000.c,v 1.23 2000/07/03 10:01:38 dwmw2 Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/types.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/doc2000.h> + +//#define PRERELEASE + +static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); +static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf); +static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); +static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); + + +static struct mtd_info *doc2klist = NULL; + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ + +int _DoC_WaitReady (unsigned long docptr) +{ + //long c=-1; + short c=-1; + + DEBUG(2,"_DoC_WaitReady called for out-of-line wait\n"); + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c) + ; + + if (c == 0) + DEBUG(2, "_DoC_WaitReady timed out.\n"); + + return (c==0); +} + +static inline int DoC_WaitReady(unsigned long docptr) +{ + /* This is inline, to optimise the common case, where it's ready instantly */ + volatile char dummy; + int ret = 0; + + /* Out-of-line routine to wait for chip response */ + /* TPW: Add 4 reads - see Software Requirement 2.3.2 */ + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + ret = _DoC_WaitReady(docptr); /* Call the out-of-line routine to wait */ + + /* TPW: Add 2 reads - see Software Requirement 2.3.2 */ + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + + return ret; +} + + +/* DoC_Command: Send a flash command to the flash chip */ + +static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags) +{ + /* Assert the CLE (Command Latch Enable) line to the flash chip */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, + docptr, CDSNControl); + + /* Send the command */ + WriteDOC(command, docptr, 2k_CDSN_IO); + + /* Lower the CLE line */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); + + /* Wait for the chip to respond */ + return DoC_WaitReady(docptr); +} + +/* DoC_Address: Set the current address for the flash chip */ + +static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) +{ + /* Assert the ALE (Address Latch Enable line to the flash chip */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, + docptr, CDSNControl); + + /* Send the address */ + /* Three cases: + numbytes == 1: Send single byte, bits 0-7. + numbytes == 2: Send bits 9-16 followed by 17-23 + numbytes == 3: Send 0-7, 9-16, then 17-23 + */ + if (numbytes != 2) + WriteDOC(ofs & 0xff, docptr, 2k_CDSN_IO); + + if (numbytes != 1) { + WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CDSN_IO); + WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CDSN_IO); + } + /* Lower the ALE line */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl); + + /* Wait for the chip to respond */ + return DoC_WaitReady(docptr); +} + +/* DoC_SelectChip: Select a given flash chip within the current floor */ + +static inline int DoC_SelectChip(unsigned long docptr, int chip) +{ + /* Select the individual flash chip requested */ + WriteDOC( chip, docptr, CDSNDeviceSelect); + + /* Wait for it to be ready */ + return DoC_WaitReady(docptr); +} + +/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ + +static inline int DoC_SelectFloor(unsigned long docptr, int floor) +{ + /* Select the floor (bank) of chips required */ + WriteDOC( floor, docptr, FloorSelect); + + /* Wait for the chip to be ready */ + return DoC_WaitReady(docptr); +} + +/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ + +int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) +{ + int mfr, id, chipshift=0; + char *mfrname=NULL, *idname=NULL; + + /* Page in the required floor/chip */ + DoC_SelectFloor(doc->virtadr, floor); + DoC_SelectChip(doc->virtadr, chip); + + /* Reset the chip */ + if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP)) { + DEBUG(2, "DoC_Command (reset) for %d,%d returned true\n", floor,chip); + return 0; + } + + /* Read the NAND chip ID: 1. Send ReadID command */ + if(DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP)) { + DEBUG(2,"DoC_Command (ReadID) for %d,%d returned true\n", floor,chip); + return 0; + } + + /* Read the NAND chip ID: 2. Send address byte zero + */ + DoC_Address(doc->virtadr, 1, 0, CDSN_CTRL_WP, 0); + + /* Read the manufacturer and device id codes from the device */ + mfr = ReadDOC(doc->virtadr, 2k_CDSN_IO); + id = ReadDOC(doc->virtadr, 2k_CDSN_IO); + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) + return 0; + + /* Check it's the same as the first chip we identified. + * M-Systems say that any given DiskOnChip device should only + * contain _one_ type of flash part, although that's not a + * hardware restriction. */ + if (doc->mfr) { + if (doc->mfr == mfr && doc->id == id) + return 1; /* This is another the same the first */ + else + printk(KERN_WARNING "Flash chip at floor %d, chip %d is different:\n", + floor, chip); + } + + /* Print (and store if first time) the manufacturer and ID codes. */ + + switch(mfr) { + case NAND_MFR_TOSHIBA: /* Toshiba */ + mfrname = "Toshiba"; + + switch(id) { + case 0x64: + idname = "TC5816BDC"; + chipshift = 21; + break; + + case 0x6b: + idname = "TC5832DC"; + chipshift = 22; + break; + + case 0x73: + idname = "TH58V128DC"; + chipshift = 24; + break; + + case 0x75: + idname = "TC58256FT/DC"; + chipshift = 25; + break; + + case 0xe5: + idname = "TC58V32DC"; + chipshift = 22; + break; + + case 0xe6: + idname = "TC58V64DC"; + chipshift = 23; + break; + + case 0xea: + idname = "TC58V16BDC"; + chipshift = 21; + break; + } + break; /* End of Toshiba parts */ + + case NAND_MFR_SAMSUNG: /* Samsung */ + mfrname = "Samsung"; + + switch(id) { + case 0x64: + idname = "KM29N16000"; + chipshift = 21; + + case 0x73: + idname = "KM29U128T"; + chipshift = 24; + break; + + case 0x75: + idname = "KM29U256T"; + chipshift = 25; + break; + + case 0xe3: + idname = "KM29W32000"; + chipshift = 22; + break; + + case 0xe6: + idname = "KM29U64000"; + chipshift = 23; + break; + + case 0xea: + idname = "KM29W16000"; + chipshift = 21; + break; + } + break; /* End of Samsung parts */ + } + + /* If we've identified it fully, print the full names */ + if (idname) { +#ifdef PRERELEASE + DEBUG(1, "Flash chip found: %2.2X %2.2X (%s %s)\n", + mfr,id,mfrname,idname); +#endif + /* If this is the first chip, store the id codes */ + if (!doc->mfr) { + doc->mfr = mfr; + doc->id = id; + doc->chipshift = chipshift; + return 1; + } + return 0; + } + + /* We haven't fully identified the chip. Print as much as we know. */ + if (mfrname) + printk(KERN_WARNING "Unknown %s flash chip found: %2.2X %2.2X\n", mfrname, + id, mfr); + else + printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", id, mfr); + + printk(KERN_WARNING "Please report to David.Woodhouse@mvhi.com\n"); + return 0; +} + +/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +void DoC_ScanChips(struct DiskOnChip *this) +{ + int floor, chip; + int numchips[MAX_FLOORS]; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0 ; floor < MAX_FLOORS ; floor++) { + ret = 1; + numchips[floor]=0; + for (chip = 0 ; chip < MAX_CHIPS && ret != 0; chip++ ) { + + ret = DoC_IdentChip(this, floor, chip); + if (ret) { + numchips[floor]++; + this->numchips++; + } + } + } + + /* If there are none at all that we recognise, bail */ + if (!this->numchips) { + printk("No flash chips recognised.\n"); + return; + } + + /* Allocate an array to hold the information for each chip */ + this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); + if (!this->chips){ + printk("No memory for allocating chip info structures\n"); + return; + } + + ret = 0; + + /* Fill out the chip array with {floor, chipno} for each + * detected chip in the device. */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + for (chip = 0 ; chip < numchips[floor] ; chip++) { + this->chips[ret].floor = floor; + this->chips[ret].chip = chip; + this->chips[ret].curadr = 0; + this->chips[ret].curmode = 0x50; + ret++; + } + } + + /* Calculate and print the total size of the device */ + this->totlen = this->numchips * (1 << this->chipshift); + + printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mb\n", this->numchips , + this->totlen >> 20); +} + + +static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) +{ + int tmp1, tmp2, retval; + if (doc1->physadr == doc2->physadr) + return 1; + + /* Use the alias resolution register which was set aside for this + * purpose. If it's value is the same on both chips, they might + * be the same chip, and we write to one and check for a change in + * the other. It's unclear if this register is usuable in the + * DoC 2000 (it's in the Millenium docs), but it seems to work. */ + tmp1 = ReadDOC(doc1->virtadr, AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, AliasResolution); + if (tmp1 != tmp2) + return 0; + + WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, AliasResolution); + if (tmp2 == (tmp1+1) % 0xff) + retval = 1; + else + retval = 0; + + /* Restore register contents. May not be necessary, but do it just to + * be safe. */ + WriteDOC(tmp1, doc1->virtadr, AliasResolution); + + return retval; +} + + +void DoC2k_init(struct mtd_info *mtd) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *old = NULL; + + /* We must avoid being called twice for the same device. */ + + if (doc2klist) + old = (struct DiskOnChip *)doc2klist->priv; + + while (old) { + if (DoC2k_is_alias(old, this)) { + printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n", + this->physadr); + iounmap((void *)this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) + old = (struct DiskOnChip *)old->nextdoc->priv; + else + old = NULL; + } + + + mtd->name = "DiskOnChip 2000"; + printk(KERN_NOTICE "DiskOnChip 2000 found at address 0x%lX\n",this->physadr); + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->size = 0; + mtd->erasesize = 0x2000; + mtd->oobblock = 512; + mtd->oobsize = 16; + mtd->module = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = doc_read; + mtd->write = doc_write; + mtd->read_ecc = doc_read_ecc; + mtd->write_ecc = doc_write_ecc; + mtd->read_oob = doc_read_oob; + mtd->write_oob = doc_write_oob; + mtd->sync = NULL; + + this->totlen = 0; + this->numchips = 0; + + this->curfloor = -1; + this->curchip = -1; + + /* Ident all the chips present. */ + DoC_ScanChips(this); + + if (!this->totlen) { + kfree(mtd); + iounmap((void *)this->virtadr); + } else { + this->nextdoc = doc2klist; + doc2klist = mtd; + mtd->size = this->totlen; + add_mtd_device(mtd); + return; + } +} + + +EXPORT_SYMBOL(DoC2k_init); + +static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + /* Just a special case of doc_read_ecc */ + return doc_read_ecc(mtd, from, len, retlen, buf, NULL); +} + +static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int di=0; /* Yes, DI is a hangover from when I was disassembling the binary driver */ + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow read past end of device */ + if (from >= this->totlen) + return -EINVAL; + + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ( (from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[from >> (this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); + WriteDOC ( DOC_ECC_EN, docptr, ECCConf); + } + + DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); + DoC_Address(docptr, 3, from, CDSN_CTRL_WP , CDSN_CTRL_ECC_IO); + + for (di=0; di < len ; di++) { + buf[di] = ReadDOC(docptr, 2k_CDSN_IO); + } + + /* Let the caller know we completed it */ + *retlen = len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di=0; di<6; di++) { + eccbuf[di] = ReadDOC(docptr, 2k_CDSN_IO); + } + + /* Flush the pipeline */ + (void) ReadDOC(docptr, 2k_ECCStatus); + (void) ReadDOC(docptr, 2k_ECCStatus); + + /* Check the ECC Status */ + if (ReadDOC(docptr, 2k_ECCStatus) & 0x80) { + /* There was an ECC error */ + printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); + + /* FIXME: Implement ECC error correction, don't just whinge */ + + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + return -EIO; + } +#ifdef PSYCHO_DEBUG + else + printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], + eccbuf[5]); +#endif + + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + + } + + return 0; +} + +static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + static char as[6]; + return doc_write_ecc(mtd, to, len, retlen, buf, as); +} + +static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int di=0; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow write past end of device */ + if (to >= this->totlen) + return -EINVAL; +#if 0 + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ( (to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + +#else + /* Don't allow writes which aren't exactly one block */ + if (to & 0x1ff || len != 0x200) + return -EINVAL; +#endif + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[to >> (this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* Set device to main plane of flash */ + DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP); + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); + WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } + + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, to, 0, CDSN_CTRL_ECC_IO); + + for (di=0; di < len ; di++) { + WriteDOC(buf[di], docptr, 2k_CDSN_IO); + } + + + if (eccbuf) { + WriteDOC( CDSN_CTRL_ECC_IO | CDSN_CTRL_CE , docptr, CDSNControl ); + +#if 1 + /* eduardp@m-sys.com says this shouldn't be necessary, + * but it doesn't actually work without it, so I've + * left it in for now. dwmw2. + */ + + WriteDOC( 0, docptr, 2k_CDSN_IO); + WriteDOC( 0, docptr, 2k_CDSN_IO); + WriteDOC( 0, docptr, 2k_CDSN_IO); +#endif + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di=0; di<6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); + } +#ifdef PSYCHO_DEBUG + printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5] ); +#endif + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + + } + + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + + DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return -EIO; + } + + /* Let the caller know we completed it */ + *retlen = len; + + return 0; +} + + + +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + + + DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0); + + for (i=0; i<len; i++) + buf[i] = ReadDOC(docptr, 2k_CDSN_IO); + + *retlen = len; + return 0; + +} + +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; + + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); + + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, ofs, 0, 0); + + for (i=0; i<len; i++) + WriteDOC(buf[i], docptr, 2k_CDSN_IO); + + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + DoC_Command(docptr, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error programming oob data\n"); + /* There was an error */ + *retlen = 0; + return -EIO; + } + + *retlen = len; + return 0; + +} + + +int doc_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + unsigned long ofs = instr->addr; + unsigned long len = instr->len; + unsigned long docptr; + struct Nand *mychip; + + if(len != mtd->erasesize) + printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize); + + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + instr->state = MTD_ERASE_PENDING; + + DoC_Command(docptr, NAND_CMD_ERASE1, 0); + DoC_Address(docptr, 2, ofs, 0, 0); + DoC_Command(docptr, NAND_CMD_ERASE2, 0); + + instr->state = MTD_ERASING; + + DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error writing\n"); + /* There was an error */ + instr->state = MTD_ERASE_FAILED; + } + else + instr->state = MTD_ERASE_DONE; + + if (instr->callback) + instr->callback(instr); + + return 0; +} + + + + + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define cleanup_doc2000 cleanup_module +#endif +#define __exit +#endif + + +static void __exit cleanup_doc2000(void) +{ + struct mtd_info *mtd; + struct DiskOnChip *this; + + while((mtd=doc2klist)) { + this = (struct DiskOnChip *)mtd->priv; + doc2klist = this->nextdoc; + + del_mtd_device(mtd); + + iounmap((void *)this->virtadr); + kfree(this->chips); + kfree(mtd); + } + +} + +#if LINUX_VERSION_CODE > 0x20300 +module_exit(cleanup_doc2000); +#endif + diff --git a/drivers/mtd/doc2001.c b/drivers/mtd/doc2001.c new file mode 100644 index 000000000..9f63b0b22 --- /dev/null +++ b/drivers/mtd/doc2001.c @@ -0,0 +1,844 @@ + +/* Linux driver for Disk-On-Chip Millennium */ +/* (c) 1999 Machine Vision Holdings, Inc. */ +/* Author: David Woodhouse <dwmw2@mvhi.com> */ +/* $Id: doc2001.c,v 1.4 2000/07/03 10:01:38 dwmw2 Exp $ */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/types.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/doc2000.h> + + + +//#define PRERELEASE +#if 0 +static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); +static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf); +static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); +static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); +#endif + +static struct mtd_info *docmillist = NULL; + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ + +int _DoC_WaitReady (unsigned long docptr) +{ + //long c=-1; + short c=-1; + + DEBUG(2,"_DoC_WaitReady called for out-of-line wait\n"); + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c) + ; + + if (c == 0) + DEBUG(2, "_DoC_WaitReady timed out.\n"); + + return (c==0); +} + +static inline int DoC_WaitReady(unsigned long docptr) +{ + /* This is inline, to optimise the common case, where it's ready instantly */ + volatile char dummy; + int ret = 0; + + /* Out-of-line routine to wait for chip response */ + /* TPW: Add 4 reads - see Software Requirement 2.3.2 */ + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + + if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) + ret = _DoC_WaitReady(docptr); /* Call the out-of-line routine to wait */ + + /* TPW: Add 2 reads - see Software Requirement 2.3.2 */ + dummy = ReadDOC(docptr, CDSNControl); + dummy = ReadDOC(docptr, CDSNControl); + + return ret; +} + + +/* DoC_Command: Send a flash command to the flash chip */ + +static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags) +{ + /* Assert the CLE (Command Latch Enable) line to the flash chip */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, + docptr, CDSNControl); + + /* Send the command */ + WriteDOC(command, docptr, 2k_CDSN_IO); + + /* Lower the CLE line */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); + + /* Wait for the chip to respond */ + return DoC_WaitReady(docptr); +} + +/* DoC_Address: Set the current address for the flash chip */ + +static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs, + unsigned char xtraflags1, unsigned char xtraflags2) +{ + /* Assert the ALE (Address Latch Enable line to the flash chip */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, + docptr, CDSNControl); + + /* Send the address */ + /* Three cases: + numbytes == 1: Send single byte, bits 0-7. + numbytes == 2: Send bits 9-16 followed by 17-23 + numbytes == 3: Send 0-7, 9-16, then 17-23 + */ + if (numbytes != 2) + WriteDOC(ofs & 0xff, docptr, 2k_CDSN_IO); + + if (numbytes != 1) { + WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CDSN_IO); + WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CDSN_IO); + } + /* Lower the ALE line */ + WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl); + + /* Wait for the chip to respond */ + return DoC_WaitReady(docptr); +} + +/* DoC_SelectChip: Select a given flash chip within the current floor */ + +static inline int DoC_SelectChip(unsigned long docptr, int chip) +{ + /* Select the individual flash chip requested */ + WriteDOC( chip, docptr, CDSNDeviceSelect); + + /* Wait for it to be ready */ + return DoC_WaitReady(docptr); +} + +/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ + +static inline int DoC_SelectFloor(unsigned long docptr, int floor) +{ + /* Select the floor (bank) of chips required */ + WriteDOC( floor, docptr, FloorSelect); + + /* Wait for the chip to be ready */ + return DoC_WaitReady(docptr); +} + +/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ + +int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) +{ + int mfr, id, chipshift=0; + char *mfrname=NULL, *idname=NULL; + + /* Page in the required floor/chip */ + DoC_SelectFloor(doc->virtadr, floor); + DoC_SelectChip(doc->virtadr, chip); + + /* Reset the chip */ + if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP)) { + DEBUG(2, "DoC_Command (reset) for %d,%d returned true\n", floor,chip); + return 0; + } + + /* Read the NAND chip ID: 1. Send ReadID command */ + if(DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP)) { + DEBUG(2,"DoC_Command (ReadID) for %d,%d returned true\n", floor,chip); + return 0; + } + + /* Read the NAND chip ID: 2. Send address byte zero + */ + DoC_Address(doc->virtadr, 1, 0, CDSN_CTRL_WP, 0); + + /* Read the manufacturer and device id codes from the device */ + mfr = ReadDOC(doc->virtadr, 2k_CDSN_IO); + id = ReadDOC(doc->virtadr, 2k_CDSN_IO); + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) + return 0; + + /* Check it's the same as the first chip we identified. + * M-Systems say that any given DiskOnChip device should only + * contain _one_ type of flash part, although that's not a + * hardware restriction. */ + if (doc->mfr) { + if (doc->mfr == mfr && doc->id == id) + return 1; /* This is another the same the first */ + else + printk(KERN_WARNING "Flash chip at floor %d, chip %d is different:\n", + floor, chip); + } + + /* Print (and store if first time) the manufacturer and ID codes. */ + + switch(mfr) { + case NAND_MFR_TOSHIBA: /* Toshiba */ + mfrname = "Toshiba"; + + switch(id) { + case 0x64: + idname = "TC5816BDC"; + chipshift = 21; + break; + + case 0x6b: + idname = "TC5832DC"; + chipshift = 22; + break; + + case 0x73: + idname = "TH58V128DC"; + chipshift = 24; + break; + + case 0x75: + idname = "TC58256FT/DC"; + chipshift = 25; + break; + + case 0xe5: + idname = "TC58V32DC"; + chipshift = 22; + break; + + case 0xe6: + idname = "TC58V64DC"; + chipshift = 23; + break; + + case 0xea: + idname = "TC58V16BDC"; + chipshift = 21; + break; + } + break; /* End of Toshiba parts */ + + case NAND_MFR_SAMSUNG: /* Samsung */ + mfrname = "Samsung"; + + switch(id) { + case 0x64: + idname = "KM29N16000"; + chipshift = 21; + + case 0x73: + idname = "KM29U128T"; + chipshift = 24; + break; + + case 0x75: + idname = "KM29U256T"; + chipshift = 25; + break; + + case 0xe3: + idname = "KM29W32000"; + chipshift = 22; + break; + + case 0xe6: + idname = "KM29U64000"; + chipshift = 23; + break; + + case 0xea: + idname = "KM29W16000"; + chipshift = 21; + break; + } + break; /* End of Samsung parts */ + } + + /* If we've identified it fully, print the full names */ + if (idname) { +#ifdef PRERELEASE + DEBUG(1, "Flash chip found: %2.2X %2.2X (%s %s)\n", + mfr,id,mfrname,idname); +#endif + /* If this is the first chip, store the id codes */ + if (!doc->mfr) { + doc->mfr = mfr; + doc->id = id; + doc->chipshift = chipshift; + return 1; + } + return 0; + } + + /* We haven't fully identified the chip. Print as much as we know. */ + if (mfrname) + printk(KERN_WARNING "Unknown %s flash chip found: %2.2X %2.2X\n", mfrname, + id, mfr); + else + printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", id, mfr); + + printk(KERN_WARNING "Please report to David.Woodhouse@mvhi.com\n"); + return 0; +} + +/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +void DoC_ScanChips(struct DiskOnChip *this) +{ + int floor, chip; + int numchips[MAX_FLOORS]; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0 ; floor < MAX_FLOORS ; floor++) { + ret = 1; + numchips[floor]=0; + for (chip = 0 ; chip < MAX_CHIPS && ret != 0; chip++ ) { + + ret = DoC_IdentChip(this, floor, chip); + if (ret) { + numchips[floor]++; + this->numchips++; + } + } + } + + /* If there are none at all that we recognise, bail */ + if (!this->numchips) { + printk("No flash chips recognised.\n"); + return; + } + + /* Allocate an array to hold the information for each chip */ + this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); + if (!this->chips){ + printk("No memory for allocating chip info structures\n"); + return; + } + + ret = 0; + + /* Fill out the chip array with {floor, chipno} for each + * detected chip in the device. */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + for (chip = 0 ; chip < numchips[floor] ; chip++) { + this->chips[ret].floor = floor; + this->chips[ret].chip = chip; + this->chips[ret].curadr = 0; + this->chips[ret].curmode = 0x50; + ret++; + } + } + + /* Calculate and print the total size of the device */ + this->totlen = this->numchips * (1 << this->chipshift); + + printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mb\n", this->numchips , + this->totlen >> 20); +} + +static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) +{ + int tmp1, tmp2, retval; + if (doc1->physadr == doc2->physadr) + return 1; + + /* Use the alias resolution register which was set aside for this + * purpose. If it's value is the same on both chips, they might + * be the same chip, and we write to one and check for a change in + * the other. It's unclear if this register is usuable in the + * DoC 2000 (it's in the Millenium docs), but it seems to work. */ + tmp1 = ReadDOC(doc1->virtadr, AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, AliasResolution); + if (tmp1 != tmp2) + return 0; + + WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, AliasResolution); + if (tmp2 == (tmp1+1) % 0xff) + retval = 1; + else + retval = 0; + + /* Restore register contents. May not be necessary, but do it just to + * be safe. */ + WriteDOC(tmp1, doc1->virtadr, AliasResolution); + + return retval; +} + + +void DoCMil_init(struct mtd_info *mtd) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *old = NULL; + + /* We must avoid being called twice for the same device. */ + + if (docmillist) + old = (struct DiskOnChip *)docmillist->priv; + + while (old) { + if (DoCMil_is_alias(this, old)) { + printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at 0x%lX - already configured\n", + this->physadr); + iounmap((void *)this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) + old = (struct DiskOnChip *)old->nextdoc->priv; + else + old = NULL; + } + + mtd->name = "DiskOnChip Millennium"; + printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",this->physadr); + +#if 1 + printk("Unfortunately, we don't have support for the DiskOnChip Millennium yet.\n"); + iounmap((void *)this->virtadr); + kfree(mtd); + return; +#else + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->size = 0; + mtd->erasesize = 0x2000; + mtd->oobblock = 512; + mtd->oobsize = 16; + mtd->module = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = doc_read; + mtd->write = doc_write; + mtd->read_ecc = doc_read_ecc; + mtd->write_ecc = doc_write_ecc; + mtd->read_oob = doc_read_oob; + mtd->write_oob = doc_write_oob; + mtd->sync = NULL; + + this->totlen = 0; + this->numchips = 0; + + this->curfloor = -1; + this->curchip = -1; + + /* Ident all the chips present. */ + DoC_ScanChips(this); + + if (!this->totlen) { + kfree(mtd); + iounmap((void *)this->virtadr); + } else { + this->nextdoc = docmillist; + docmillist = mtd; + mtd->size = this->totlen; + add_mtd_device(mtd); + return; + } +#endif +} + + +EXPORT_SYMBOL(DoCMil_init); +#if 0 +static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + /* Just a special case of doc_read_ecc */ + return doc_read_ecc(mtd, from, len, retlen, buf, NULL); +} + +static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int di=0; /* Yes, DI is a hangover from when I was disassembling the binary driver */ + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow read past end of device */ + if (from >= this->totlen) + return -EINVAL; + + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ( (from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[from >> (this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); + WriteDOC ( DOC_ECC_EN, docptr, ECCConf); + } + + DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP); + DoC_Address(docptr, 3, from, CDSN_CTRL_WP , CDSN_CTRL_ECC_IO); + + for (di=0; di < len ; di++) { + buf[di] = ReadDOC(docptr, 2k_CDSN_IO); + } + + /* Let the caller know we completed it */ + *retlen = len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di=0; di<6; di++) { + eccbuf[di] = ReadDOC(docptr, 2k_CDSN_IO); + } + + /* Flush the pipeline */ + (void) ReadDOC(docptr, 2k_ECCStatus); + (void) ReadDOC(docptr, 2k_ECCStatus); + + /* Check the ECC Status */ + if (ReadDOC(docptr, 2k_ECCStatus) & 0x80) { + /* There was an ECC error */ + printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); + + /* FIXME: Implement ECC error correction, don't just whinge */ + + /* We return error, but have actually done the read. Not that + this can be told to user-space, via sys_read(), but at least + MTD-aware stuff can know about it by checking *retlen */ + return -EIO; + } +#ifdef PSYCHO_DEBUG + else + printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4], + eccbuf[5]); +#endif + + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + + } + + return 0; +} + +static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + static char as[6]; + return doc_write_ecc(mtd, to, len, retlen, buf, as); +} + +static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int di=0; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + /* Don't allow write past end of device */ + if (to >= this->totlen) + return -EINVAL; +#if 0 + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ( (to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + +#else + /* Don't allow writes which aren't exactly one block */ + if (to & 0x1ff || len != 0x200) + return -EINVAL; +#endif + + /* Find the chip which is to be used and select it */ + mychip = &this->chips[to >> (this->chipshift)]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + /* Set device to main plane of flash */ + DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP); + + if (eccbuf) { + /* Prime the ECC engine */ + WriteDOC ( DOC_ECC_RESET, docptr, ECCConf); + WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); + } + + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, to, 0, CDSN_CTRL_ECC_IO); + + for (di=0; di < len ; di++) { + WriteDOC(buf[di], docptr, 2k_CDSN_IO); + } + + + if (eccbuf) { + WriteDOC( CDSN_CTRL_ECC_IO | CDSN_CTRL_CE , docptr, CDSNControl ); + +#if 1 + /* eduardp@m-sys.com says this shouldn't be necessary, + * but it doesn't actually work without it, so I've + * left it in for now. dwmw2. + */ + + WriteDOC( 0, docptr, 2k_CDSN_IO); + WriteDOC( 0, docptr, 2k_CDSN_IO); + WriteDOC( 0, docptr, 2k_CDSN_IO); +#endif + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di=0; di<6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); + } +#ifdef PSYCHO_DEBUG + printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + (long) to, eccbuf[0], eccbuf[1], eccbuf[2], + eccbuf[3], eccbuf[4], eccbuf[5] ); +#endif + /* Reset the ECC engine */ + WriteDOC(DOC_ECC_RESV, docptr , ECCConf); + + } + + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + + DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error programming flash\n"); + /* Error in programming */ + *retlen = 0; + return -EIO; + } + + /* Let the caller know we completed it */ + *retlen = len; + + return 0; +} + + + +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + + + DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); + DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0); + + for (i=0; i<len; i++) + buf[i] = ReadDOC(docptr, 2k_CDSN_IO); + + *retlen = len; + return 0; + +} + +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + int i; + unsigned long docptr; + struct Nand *mychip; + + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP); + DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP); + + DoC_Command(docptr, NAND_CMD_SEQIN, 0); + DoC_Address(docptr, 3, ofs, 0, 0); + + for (i=0; i<len; i++) + WriteDOC(buf[i], docptr, 2k_CDSN_IO); + + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0); + DoC_Command(docptr, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error programming oob data\n"); + /* There was an error */ + *retlen = 0; + return -EIO; + } + + *retlen = len; + return 0; + +} + + +int doc_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + unsigned long ofs = instr->addr; + unsigned long len = instr->len; + unsigned long docptr; + struct Nand *mychip; + + if(len != mtd->erasesize) + printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize); + + + docptr = this->virtadr; + + mychip = &this->chips[ofs >> this->chipshift]; + + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } + else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + instr->state = MTD_ERASE_PENDING; + + DoC_Command(docptr, NAND_CMD_ERASE1, 0); + DoC_Address(docptr, 2, ofs, 0, 0); + DoC_Command(docptr, NAND_CMD_ERASE2, 0); + + instr->state = MTD_ERASING; + + DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP); + + if (ReadDOC(docptr, 2k_CDSN_IO) & 1) { + printk("Error writing\n"); + /* There was an error */ + instr->state = MTD_ERASE_FAILED; + } + else + instr->state = MTD_ERASE_DONE; + + if (instr->callback) + instr->callback(instr); + + return 0; +} + + + +#endif + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define cleanup_doc2001 cleanup_module +#endif +#define __exit +#endif + + +static void __exit cleanup_doc2001(void) +{ + struct mtd_info *mtd; + struct DiskOnChip *this; + + while((mtd=docmillist)) { + this = (struct DiskOnChip *)mtd->priv; + docmillist = this->nextdoc; + + del_mtd_device(mtd); + + iounmap((void *)this->virtadr); + kfree(this->chips); + kfree(mtd); + } + +} + +#if LINUX_VERSION_CODE > 0x20300 +module_exit(cleanup_doc2001); +#endif + diff --git a/drivers/mtd/docprobe.c b/drivers/mtd/docprobe.c new file mode 100644 index 000000000..b2c4380d3 --- /dev/null +++ b/drivers/mtd/docprobe.c @@ -0,0 +1,269 @@ + +/* Linux driver for Disk-On-Chip devices */ +/* Probe routines common to all DoC devices */ +/* (c) 1999 Machine Vision Holdings, Inc. */ +/* Author: David Woodhouse <dwmw2@mvhi.com> */ +/* $Id: docprobe.c,v 1.8 2000/06/26 20:40:53 dwmw2 Exp $ */ + + + +/* DOC_PASSIVE_PROBE: + In order to ensure that the BIOS checksum is correct at boot time, and + hence that the onboard BIOS extension gets executed, the DiskOnChip + goes into reset mode when it is read sequentially: all registers + return 0xff until the chip is woken up again by writing to the + DOCControl register. + + Unfortunately, this means that the probe for the DiskOnChip is unsafe, + because one of the first things it does is write to where it thinks + the DOCControl register should be - which may well be shared memory + for another device. I've had machines which lock up when this is + attempted. Hence the possibility to do a passive probe, which will fail + to detect a chip in reset mode, but is at least guaranteed not to lock + the machine. + + If you have this problem, uncomment the following line: +#define DOC_PASSIVE_PROBE +*/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kmod.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/types.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/doc2000.h> + +/* Where to look for the devices? */ + +#if defined (__alpha__) || defined(__i386__) +static unsigned long __initdata doc_locations[] = { + 0xc8000, 0xca000, 0xcc000, 0xce000, + 0xd0000, 0xd2000, 0xd4000, 0xd6000, + 0xd8000, 0xda000, 0xdc000, 0xde000, + 0xe0000, 0xe2000, 0xe4000, 0xe6000, + 0xe8000, 0xea000, 0xec000, 0xee000, 0 }; +#elif defined(__ppc__) +static unsigned long __initdata doc_locations[] = { + 0xe4000000, 0}; +#else +#warning Unknown architecture for DiskOnChip. No default probe locations defined +#endif + +#ifdef CONFIG_MTD_DOC2000 +extern void DoC2k_init(struct mtd_info *); +#endif +#ifdef CONFIG_MTD_DOC2001 +extern void DoCMil_init(struct mtd_info *); +#endif + +/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ + +static inline int __init doccheck(unsigned long potential, unsigned long physadr) +{ + unsigned long window=potential; + unsigned char tmp, ChipID; +#ifndef DOC_PASSIVE_PROBE + unsigned char tmp2; +#endif + + /* Routine copied from the Linux DOC driver */ + + /* Check for 0x55 0xAA signature at beginning of window */ + if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa) + return 0; + +#ifndef DOC_PASSIVE_PROBE + /* It's not possible to cleanly detect the DiskOnChip - the + * bootup procedure will put the device into reset mode, and + * it's not possible to talk to it without actually writing + * to the DOCControl register. So we store the current contents + * of the DOCControl register's location, in case we later decide + * that it's not a DiskOnChip, and want to put it back how we + * found it. + */ + tmp2 = ReadDOC(window, DOCControl); + + /* Reset the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + window, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, + window, DOCControl); + + /* Enable the DiskOnChip ASIC */ + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + window, DOCControl); + WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, + window, DOCControl); +#endif /* !DOC_PASSIVE_PROBE */ + + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; + if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp) + return ChipID; + break; + + case DOC_ChipID_DocMil: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; + if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) + return ChipID; + break; + + default: + printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + ChipID, physadr); + +#ifndef DOC_PASSIVE_PROBE + /* Put back the contents of the DOCControl register, in case it's not + * actually a DiskOnChip. + */ + WriteDOC(tmp2, window, DOCControl); +#endif + return 0; + } + + printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n"); + +#ifndef DOC_PASSIVE_PROBE + /* Put back the contents of the DOCControl register: it's not a DiskOnChip */ + WriteDOC(tmp2, window, DOCControl); +#endif + return 0; +} + + +static void DoC_Probe(unsigned long physadr) +{ + unsigned long docptr; + struct DiskOnChip *this; + struct mtd_info *mtd; + int ChipID; + char namebuf[15]; + char *name = namebuf; + void (*initroutine)(struct mtd_info *) = NULL; + int initroutinedynamic = 0; + + docptr = (unsigned long)ioremap(physadr, 0x2000); + + if (!docptr) + return; + + if ((ChipID = doccheck(docptr, physadr))) { + + mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); + + if (!mtd) { + printk("Cannot allocate memory for data structures. Dropping.\n"); + iounmap((void *)docptr); + return; + } + + this = (struct DiskOnChip *)(&mtd[1]); + + memset((char *)mtd,0, sizeof(struct mtd_info)); + memset((char *)this, 0, sizeof(struct DiskOnChip)); + + mtd->priv = this; + this->virtadr = docptr; + this->physadr = physadr; + this->ChipID = ChipID; + sprintf(namebuf, "with ChipID %2.2X", ChipID); + + switch(ChipID) { + case DOC_ChipID_Doc2k: + name="2000"; +#ifdef CONFIG_MTD_DOC2000 + initroutine = &DoC2k_init; +#elif CONFIG_MODULES + initroutinedynamic=1; + initroutine = (void *)get_module_symbol(NULL, "DoC2k_init"); +#ifdef CONFIG_KMOD + if (!initroutine) { + request_module("doc2000"); + initroutine = (void *)get_module_symbol("doc2000", "DoC2k_init"); + } +#endif /* CONFIG_KMOD */ +#endif + break; + + case DOC_ChipID_DocMil: + name="Millennium"; +#ifdef CONFIG_MTD_DOC2001 + initroutine = &DocMil_init; +#elif CONFIG_MODULES + initroutinedynamic=1; + initroutine = (void *)get_module_symbol(NULL, "DoCMil_init"); +#ifdef CONFIG_KMOD + if (!initroutine) { + request_module("doc2001"); + initroutine = (void *)get_module_symbol("doc2001", "DoCMil_init"); + } +#endif /* CONFIG_KMOD */ +#endif + break; + } + if (initroutine) { + (*initroutine)(mtd); +#if defined(CONFIG_MODULES) && LINUX_VERSION_CODE >= 0x20400 + if (initroutinedynamic) + put_module_symbol(initroutine); +#endif + return; + } + printk("Cannot find driver for DiskOnChip %s at 0x%X\n", name, physadr); + } + iounmap((void *)docptr); +} + + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_doc init_module +#endif +#define __exit +#endif + +int __init init_doc(void) +{ + int i; + + printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n"); +#ifdef PRERELEASE + printk(KERN_INFO "$Id: docprobe.c,v 1.8 2000/06/26 20:40:53 dwmw2 Exp $\n"); +#endif + + for (i=0; doc_locations[i]; i++) { + DoC_Probe(doc_locations[i]); + } + + return 0; + +} + + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_doc); +#endif + diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c new file mode 100644 index 000000000..1cf4a5f16 --- /dev/null +++ b/drivers/mtd/ftl.c @@ -0,0 +1,1458 @@ +/* This version ported to the Linux-MTD system by dwmw2@infradead.org + * $Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $ + * Based on: + */ +/*====================================================================== + + A Flash Translation Layer memory card driver + + This driver implements a disk-like block device driver with an + apparent block size of 512 bytes for flash memory cards. + + ftl_cs.c 1.62 2000/02/01 00:59:04 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + <dhinds@pcmcia.sourceforge.org>. Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + + LEGAL NOTE: The FTL format is patented by M-Systems. They have + granted a license for its use with PCMCIA devices: + + "M-Systems grants a royalty-free, non-exclusive license under + any presently existing M-Systems intellectual property rights + necessary for the design and development of FTL-compatible + drivers, file systems and utilities using the data formats with + PCMCIA PC Cards as described in the PCMCIA Flash Translation + Layer (FTL) Specification." + + Use of the FTL format for non-PCMCIA applications may be an + infringement of these patents. For additional information, + contact M-Systems (http://www.m-sys.com) directly. + +======================================================================*/ +#define FTL_DEBUG 5 +#ifdef FTL_DEBUG +#define DEBUGLVL debug +#endif + +#include <linux/module.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> +/*#define PSYCHO_DEBUG */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/hdreg.h> +#include <stdarg.h> + +#if (LINUX_VERSION_CODE >= 0x20100) +#include <linux/vmalloc.h> +#endif +#if (LINUX_VERSION_CODE >= 0x20303) +#include <linux/blkpg.h> +#endif + +#include <linux/mtd/ftl.h> +/*====================================================================*/ +/* Stuff which really ought to be in compatmac.h */ + +#if (LINUX_VERSION_CODE < 0x20328) +#define register_disk(dev, drive, minors, ops, size) \ + do { (dev)->part[(drive)*(minors)].nr_sects = size; \ + if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ + resetup_one_dev(dev, drive); } while (0); +#endif + +#if (LINUX_VERSION_CODE < 0x20320) +#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +#define blk_init_queue(q, req) q = (req) +#define blk_cleanup_queue(q) q = NULL +#define request_arg_t void +#else +#define request_arg_t request_queue_t *q +#endif + + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Major device # for FTL device */ +static int shuffle_freq = 50; + +MODULE_PARM(shuffle_freq, "i"); + +/*====================================================================*/ + +#ifndef FTL_MAJOR +#define FTL_MAJOR 44 +#endif + + +/* Funky stuff for setting up a block device */ +#define MAJOR_NR FTL_MAJOR +#define DEVICE_NAME "ftl" +#define DEVICE_REQUEST do_ftl_request +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#define DEVICE_NR(minor) ((minor)>>5) +#define REGION_NR(minor) (((minor)>>3)&3) +#define PART_NR(minor) ((minor)&7) +#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part)) + +#include <linux/blk.h> + +#ifdef FTL_DEBUG +static int debug = FTL_DEBUG; +MODULE_PARM(debug, "i"); +#endif + +/*====================================================================*/ + +#ifndef FTL_MAJOR +#define FTL_MAJOR 44 +#endif + +/* Maximum number of separate memory devices we'll allow */ +#define MAX_DEV 4 + +/* Maximum number of regions per device */ +#define MAX_REGION 4 + +/* Maximum number of partitions in an FTL region */ +#define PART_BITS 3 +#define MAX_PART 8 + +/* Maximum number of outstanding erase requests per socket */ +#define MAX_ERASE 8 + +/* Sector size -- shouldn't need to change */ +#define SECTOR_SIZE 512 + + +/* Each memory region corresponds to a minor device */ +typedef struct partition_t { + struct mtd_info *mtd; + u_int32_t state; + u_int32_t *VirtualBlockMap; + u_int32_t *VirtualPageMap; + u_int32_t FreeTotal; + struct eun_info_t { + u_int32_t Offset; + u_int32_t EraseCount; + u_int32_t Free; + u_int32_t Deleted; + } *EUNInfo; + struct xfer_info_t { + u_int32_t Offset; + u_int32_t EraseCount; + u_int16_t state; + } *XferInfo; + u_int16_t bam_index; + u_int32_t *bam_cache; + u_int16_t DataUnits; + u_int32_t BlocksPerUnit; + erase_unit_header_t header; +#if 0 + region_info_t region; + memory_handle_t handle; +#endif + atomic_t open; +} partition_t; + +partition_t *myparts[MAX_MTD_DEVICES]; + +static void ftl_notify_add(struct mtd_info *mtd); +static void ftl_notify_remove(struct mtd_info *mtd); + +void ftl_freepart(partition_t *part); + +static struct mtd_notifier ftl_notifier={ftl_notify_add, ftl_notify_remove, NULL}; + +/* Partition state flags */ +#define FTL_FORMATTED 0x01 + +/* Transfer unit states */ +#define XFER_UNKNOWN 0x00 +#define XFER_ERASING 0x01 +#define XFER_ERASED 0x02 +#define XFER_PREPARED 0x03 +#define XFER_FAILED 0x04 + +static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)]; +static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)]; +static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)]; + +static struct gendisk ftl_gendisk = { + major: FTL_MAJOR, + major_name: "ftl", + minor_shift: PART_BITS, + max_p: MAX_PART, +#if (LINUX_VERSION_CODE < 0x20328) + max_nr: MAX_DEV*MAX_PART, +#endif + part: ftl_hd, + sizes: ftl_sizes, + nr_real: 0 +}; + +/*====================================================================*/ + +static int ftl_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg); +static int ftl_open(struct inode *inode, struct file *file); +static release_t ftl_close(struct inode *inode, struct file *file); +static int ftl_reread_partitions(int minor); + +static void ftl_erase_callback(struct erase_info *done); + +#if LINUX_VERSION_CODE < 0x20326 +static struct file_operations ftl_blk_fops = { + open: ftl_open, + release: ftl_close, + ioctl: ftl_ioctl, + read: block_read, + write: block_write, + fsync: block_fsync +}; +#else +static struct block_device_operations ftl_blk_fops = { + open: ftl_open, + release: ftl_close, + ioctl: ftl_ioctl, +}; +#endif + +/*====================================================================== + + Scan_header() checks to see if a memory region contains an FTL + partition. build_maps() reads all the erase unit headers, builds + the erase unit map, and then builds the virtual page map. + +======================================================================*/ + +static int scan_header(partition_t *part) +{ + erase_unit_header_t header; + loff_t offset; + int ret; + part->header.FormattedSize = 0; + /* Search first megabyte for a valid FTL header */ + for (offset = 0; + offset < 0x100000; + offset += part->mtd->erasesize?part->mtd->erasesize:0x2000) { + + ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, + (unsigned char *)&header); + + if (ret) + return ret; + + if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break; + } + + if (offset == 0x100000) { + printk(KERN_NOTICE "ftl_cs: FTL header not found.\n"); + return -ENOENT; + } + if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 || + (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || + (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { + printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); + return -1; + } + if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { + printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %lx\n", + 1 << header.EraseUnitSize,part->mtd->erasesize); + return -1; + } + part->header = header; + return 0; +} + +static int build_maps(partition_t *part) +{ + erase_unit_header_t header; + u_int16_t xvalid, xtrans, i; + u_int blocks, j; + int hdr_ok, ret; + ssize_t retval; + loff_t offset; + + /* Set up erase unit maps */ + part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) - + part->header.NumTransferUnits; + part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t), + GFP_KERNEL); + if (!part->EUNInfo) return -1; + for (i = 0; i < part->DataUnits; i++) + part->EUNInfo[i].Offset = 0xffffffff; + part->XferInfo = + kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t), + GFP_KERNEL); + if (!part->XferInfo) return -1; + + xvalid = xtrans = 0; + for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { + offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) + << part->header.EraseUnitSize); + ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, + (unsigned char *)&header); + + if (ret) + return ret; + + /* Is this a transfer partition? */ + hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0); + if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) && + (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) { + part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset; + part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount = + le32_to_cpu(header.EraseCount); + xvalid++; + } else { + if (xtrans == part->header.NumTransferUnits) { + printk(KERN_NOTICE "ftl_cs: format error: too many " + "transfer units!\n"); + return -1; + } + if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) { + part->XferInfo[xtrans].state = XFER_PREPARED; + part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount); + } else { + part->XferInfo[xtrans].state = XFER_UNKNOWN; + /* Pick anything reasonable for the erase count */ + part->XferInfo[xtrans].EraseCount = + le32_to_cpu(part->header.EraseCount); + } + part->XferInfo[xtrans].Offset = offset; + xtrans++; + } + } + /* Check for format trouble */ + header = part->header; + if ((xtrans != header.NumTransferUnits) || + (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) { + printk(KERN_NOTICE "ftl_cs: format error: erase units " + "don't add up!\n"); + return -1; + } + + /* Set up virtual page map */ + blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; + part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t)); + memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t)); + part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; + + part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t), + GFP_KERNEL); + if (!part->bam_cache) return -1; + + part->bam_index = 0xffff; + part->FreeTotal = 0; + + for (i = 0; i < part->DataUnits; i++) { + part->EUNInfo[i].Free = 0; + part->EUNInfo[i].Deleted = 0; + offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); + + ret = part->mtd->read(part->mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), &retval, + (unsigned char *)part->bam_cache); + + if (ret) + return ret; + + for (j = 0; j < part->BlocksPerUnit; j++) { + if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) { + part->EUNInfo[i].Free++; + part->FreeTotal++; + } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) && + (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks)) + part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] = + (i << header.EraseUnitSize) + (j << header.BlockSize); + else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j]))) + part->EUNInfo[i].Deleted++; + } + } + + return 0; + +} /* build_maps */ + +/*====================================================================== + + Erase_xfer() schedules an asynchronous erase operation for a + transfer unit. + +======================================================================*/ + +static int erase_xfer(partition_t *part, + u_int16_t xfernum) +{ + int ret; + struct xfer_info_t *xfer; + struct erase_info *erase; + + xfer = &part->XferInfo[xfernum]; + DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset); + xfer->state = XFER_ERASING; + + /* Is there a free erase slot? Always in MTD. */ + + + erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); + if (!erase) + return -ENOMEM; + + erase->callback = ftl_erase_callback; + erase->addr = xfer->Offset; + erase->len = 1 << part->header.EraseUnitSize; + erase->priv = (u_long)part; + + ret = part->mtd->erase(part->mtd, erase); + + if (!ret) + xfer->EraseCount++; + else + kfree(erase); + + return ret; +} /* erase_xfer */ + +/*====================================================================== + + Prepare_xfer() takes a freshly erased transfer unit and gives + it an appropriate header. + +======================================================================*/ + +static void ftl_erase_callback(struct erase_info *erase) +{ + partition_t *part; + struct xfer_info_t *xfer; + int i; + + /* Look up the transfer unit */ + part = (partition_t *)(erase->priv); + + for (i = 0; i < part->header.NumTransferUnits; i++) + if (part->XferInfo[i].Offset == erase->addr) break; + + if (i == part->header.NumTransferUnits) { + printk(KERN_NOTICE "ftl_cs: internal error: " + "erase lookup failed!\n"); + return; + } + + xfer = &part->XferInfo[i]; + if (erase->state == MTD_ERASE_DONE) + xfer->state = XFER_ERASED; + else { + xfer->state = XFER_FAILED; + printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n", + erase->state); + } + + kfree(erase); + +} /* ftl_erase_callback */ + +static int prepare_xfer(partition_t *part, int i) +{ + erase_unit_header_t header; + struct xfer_info_t *xfer; + int nbam, ret; + u_int32_t ctl; + ssize_t retlen; + loff_t offset; + + xfer = &part->XferInfo[i]; + xfer->state = XFER_FAILED; + + DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset); + + /* Write the transfer unit header */ + header = part->header; + header.LogicalEUN = cpu_to_le16(0xffff); + header.EraseCount = cpu_to_le32(xfer->EraseCount); + + ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header), + &retlen, (u_char *)&header); + + if (ret) { + return ret; + } + + /* Write the BAM stub */ + nbam = (part->BlocksPerUnit * sizeof(u_int32_t) + + le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE; + + offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); + ctl = cpu_to_le32(BLOCK_CONTROL); + + for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { + + ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&ctl); + + if (ret) + return ret; + } + xfer->state = XFER_PREPARED; + return 0; + +} /* prepare_xfer */ + +/*====================================================================== + + Copy_erase_unit() takes a full erase block and a transfer unit, + copies everything to the transfer unit, then swaps the block + pointers. + + All data blocks are copied to the corresponding blocks in the + target unit, so the virtual block map does not need to be + updated. + +======================================================================*/ + +static int copy_erase_unit(partition_t *part, u_int16_t srcunit, + u_int16_t xferunit) +{ + u_char buf[SECTOR_SIZE]; + struct eun_info_t *eun; + struct xfer_info_t *xfer; + u_int32_t src, dest, free, i; + u_int16_t unit; + int ret; + ssize_t retlen; + loff_t offset; + u_int16_t srcunitswap = cpu_to_le16(srcunit); + + eun = &part->EUNInfo[srcunit]; + xfer = &part->XferInfo[xferunit]; + DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n", + eun->Offset, xfer->Offset); + + + /* Read current BAM */ + if (part->bam_index != srcunit) { + + offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); + + ret = part->mtd->read(part->mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); + + /* mark the cache bad, in case we get an error later */ + part->bam_index = 0xffff; + + if (ret) { + printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n"); + return ret; + } + } + + /* Write the LogicalEUN for the transfer unit */ + xfer->state = XFER_UNKNOWN; + offset = xfer->Offset + 20; /* Bad! */ + unit = cpu_to_le16(0x7fff); + + ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t), + &retlen, (u_char *) &unit); + + if (ret) { + printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n"); + return ret; + } + + /* Copy all data blocks from source unit to transfer unit */ + src = eun->Offset; dest = xfer->Offset; + + free = 0; + ret = 0; + for (i = 0; i < part->BlocksPerUnit; i++) { + switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) { + case BLOCK_CONTROL: + /* This gets updated later */ + break; + case BLOCK_DATA: + case BLOCK_REPLACEMENT: + ret = part->mtd->read(part->mtd, src, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n"); + return ret; + } + + + ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n"); + return ret; + } + + break; + default: + /* All other blocks must be free */ + part->bam_cache[i] = cpu_to_le32(0xffffffff); + free++; + break; + } + src += SECTOR_SIZE; + dest += SECTOR_SIZE; + } + + /* Write the BAM to the transfer unit */ + ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(int32_t), &retlen, + (u_char *)part->bam_cache); + if (ret) { + printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n"); + return ret; + } + + + /* All clear? Then update the LogicalEUN again */ + ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t), + &retlen, (u_char *)&srcunitswap); + + if (ret) { + printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n"); + return ret; + } + + + /* Update the maps and usage stats*/ + i = xfer->EraseCount; + xfer->EraseCount = eun->EraseCount; + eun->EraseCount = i; + i = xfer->Offset; + xfer->Offset = eun->Offset; + eun->Offset = i; + part->FreeTotal -= eun->Free; + part->FreeTotal += free; + eun->Free = free; + eun->Deleted = 0; + + /* Now, the cache should be valid for the new block */ + part->bam_index = srcunit; + + return 0; +} /* copy_erase_unit */ + +/*====================================================================== + + reclaim_block() picks a full erase unit and a transfer unit and + then calls copy_erase_unit() to copy one to the other. Then, it + schedules an erase on the expired block. + + What's a good way to decide which transfer unit and which erase + unit to use? Beats me. My way is to always pick the transfer + unit with the fewest erases, and usually pick the data unit with + the most deleted blocks. But with a small probability, pick the + oldest data unit instead. This means that we generally postpone + the next reclaimation as long as possible, but shuffle static + stuff around a bit for wear leveling. + +======================================================================*/ + +static int reclaim_block(partition_t *part) +{ + u_int16_t i, eun, xfer; + u_int32_t best; + int queued, ret; + + DEBUG(0, "ftl_cs: reclaiming space...\n"); + DEBUG(4, "NumTransferUnits == %x\n", part->header.NumTransferUnits); + /* Pick the least erased transfer unit */ + best = 0xffffffff; xfer = 0xffff; + do { + queued = 0; + for (i = 0; i < part->header.NumTransferUnits; i++) { + int n=0; + if (part->XferInfo[i].state == XFER_UNKNOWN) { + DEBUG(4,"XferInfo[%d].state == XFER_UNKNOWN\n",i); + n=1; + erase_xfer(part, i); + } + if (part->XferInfo[i].state == XFER_ERASING) { + DEBUG(4,"XferInfo[%d].state == XFER_ERASING\n",i); + n=1; + queued = 1; + } + else if (part->XferInfo[i].state == XFER_ERASED) { + DEBUG(4,"XferInfo[%d].state == XFER_ERASED\n",i); + n=1; + prepare_xfer(part, i); + } + if (part->XferInfo[i].state == XFER_PREPARED) { + DEBUG(4,"XferInfo[%d].state == XFER_PREPARED\n",i); + n=1; + if (part->XferInfo[i].EraseCount <= best) { + best = part->XferInfo[i].EraseCount; + xfer = i; + } + } + if (!n) + DEBUG(4,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state); + + } + if (xfer == 0xffff) { + if (queued) { + DEBUG(1, "ftl_cs: waiting for transfer " + "unit to be prepared...\n"); + if (part->mtd->sync) + part->mtd->sync(part->mtd); + } else { + static int ne = 0; + if (++ne < 5) + printk(KERN_NOTICE "ftl_cs: reclaim failed: no " + "suitable transfer units!\n"); + else + DEBUG(1, "ftl_cs: reclaim failed: no " + "suitable transfer units!\n"); + + return -EIO; + } + } + } while (xfer == 0xffff); + + eun = 0; + if ((jiffies % shuffle_freq) == 0) { + DEBUG(1, "ftl_cs: recycling freshest block...\n"); + best = 0xffffffff; + for (i = 0; i < part->DataUnits; i++) + if (part->EUNInfo[i].EraseCount <= best) { + best = part->EUNInfo[i].EraseCount; + eun = i; + } + } else { + best = 0; + for (i = 0; i < part->DataUnits; i++) + if (part->EUNInfo[i].Deleted >= best) { + best = part->EUNInfo[i].Deleted; + eun = i; + } + if (best == 0) { + static int ne = 0; + if (++ne < 5) + printk(KERN_NOTICE "ftl_cs: reclaim failed: " + "no free blocks!\n"); + else + DEBUG(1,"ftl_cs: reclaim failed: " + "no free blocks!\n"); + + return -EIO; + } + } + ret = copy_erase_unit(part, eun, xfer); + if (!ret) + erase_xfer(part, xfer); + else + printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n"); + return ret; +} /* reclaim_block */ + +/*====================================================================== + + Find_free() searches for a free block. If necessary, it updates + the BAM cache for the erase unit containing the free block. It + returns the block index -- the erase unit is just the currently + cached unit. If there are no free blocks, it returns 0 -- this + is never a valid data block because it contains the header. + +======================================================================*/ + +#ifdef PSYCHO_DEBUG +static void dump_lists(partition_t *part) +{ + int i; + printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal); + for (i = 0; i < part->DataUnits; i++) + printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, " + "%d deleted\n", i, + part->EUNInfo[i].Offset >> part->header.EraseUnitSize, + part->EUNInfo[i].Free, part->EUNInfo[i].Deleted); +} +#endif + +static u_int32_t find_free(partition_t *part) +{ + u_int16_t stop, eun; + u_int32_t blk; + size_t retlen; + int ret; + + /* Find an erase unit with some free space */ + stop = (part->bam_index == 0xffff) ? 0 : part->bam_index; + eun = stop; + do { + if (part->EUNInfo[eun].Free != 0) break; + /* Wrap around at end of table */ + if (++eun == part->DataUnits) eun = 0; + } while (eun != stop); + + if (part->EUNInfo[eun].Free == 0) + return 0; + + /* Is this unit's BAM cached? */ + if (eun != part->bam_index) { + /* Invalidate cache */ + part->bam_index = 0xffff; + + ret = part->mtd->read(part->mtd, + part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); + + if (ret) { + printk(KERN_WARNING"ftl: Error reading BAM in find_free\n"); + return 0; + } + part->bam_index = eun; + } + + /* Find a free block */ + for (blk = 0; blk < part->BlocksPerUnit; blk++) + if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break; + if (blk == part->BlocksPerUnit) { +#ifdef PSYCHO_DEBUG + static int ne = 0; + if (++ne == 1) + dump_lists(part); +#endif + printk(KERN_NOTICE "ftl_cs: bad free list!\n"); + return 0; + } + DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun); + return blk; + +} /* find_free */ + +/*====================================================================== + + This gets a memory handle for the region corresponding to the + minor device number. + +======================================================================*/ + +static int ftl_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *partition; + + if (minor>>4 >= MAX_MTD_DEVICES) + return -ENODEV; + + partition = myparts[minor>>4]; + + if (!partition) + return -ENODEV; + + if (partition->state != FTL_FORMATTED) + return -ENXIO; + + if (ftl_gendisk.part[minor].nr_sects == 0) + return -ENXIO; + + MOD_INC_USE_COUNT; + + if (!get_mtd_device(partition->mtd, -1)) { + MOD_DEC_USE_COUNT; + return /* -E'SBUGGEREDOFF */ -ENXIO; + } + + if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { + put_mtd_device(partition->mtd); + MOD_DEC_USE_COUNT; + return -EROFS; + } + + DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); + + atomic_inc(&partition->open); + + return 0; +} + +/*====================================================================*/ + +static release_t ftl_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + struct super_block *sb = get_super(inode->i_rdev); +#endif + int minor = MINOR(inode->i_rdev); + partition_t *part = myparts[minor >> 4]; + int i; + + DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); + + /* Flush all writes */ + fsync_dev(inode->i_rdev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (sb) invalidate_inodes(sb); +#endif + invalidate_buffers(inode->i_rdev); + + /* Wait for any pending erase operations to complete */ + if (part->mtd->sync) + part->mtd->sync(part->mtd); + + for (i = 0; i < part->header.NumTransferUnits; i++) { + if (part->XferInfo[i].state == XFER_ERASED) + prepare_xfer(part, i); + } + + atomic_dec(&part->open); + + put_mtd_device(part->mtd); + MOD_DEC_USE_COUNT; + release_return(0); +} /* ftl_close */ + + +/*====================================================================== + + Read a series of sectors from an FTL partition. + +======================================================================*/ + +static int ftl_read(partition_t *part, caddr_t buffer, + u_long sector, u_long nblocks) +{ + u_int32_t log_addr, bsize; + u_long i; + int ret; + size_t offset, retlen; + + DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n", + part, sector, nblocks); + if (!(part->state & FTL_FORMATTED)) { + printk(KERN_NOTICE "ftl_cs: bad partition\n"); + return -EIO; + } + bsize = 1 << part->header.EraseUnitSize; + + for (i = 0; i < nblocks; i++) { + if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) { + printk(KERN_NOTICE "ftl_cs: bad read offset\n"); + return -EIO; + } + log_addr = part->VirtualBlockMap[sector+i]; + if (log_addr == 0xffffffff) + memset(buffer, 0, SECTOR_SIZE); + else { + offset = (part->EUNInfo[log_addr / bsize].Offset + + (log_addr % bsize)); + ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, + &retlen, (u_char *) buffer); + + if (ret) { + printk(KERN_WARNING "Error reading MTD device in ftl_read()\n"); + return ret; + } + } + buffer += SECTOR_SIZE; + } + return 0; +} /* ftl_read */ + +/*====================================================================== + + Write a series of sectors to an FTL partition + +======================================================================*/ + +static int set_bam_entry(partition_t *part, u_int32_t log_addr, + u_int32_t virt_addr) +{ + u_int32_t bsize, blk; +#ifdef PSYCHO_DEBUG + u_int32_t old_addr; +#endif + u_int16_t eun; + int ret; + size_t retlen, offset; + + DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n", + part, log_addr, virt_addr); + bsize = 1 << part->header.EraseUnitSize; + eun = log_addr / bsize; + blk = (log_addr % bsize) / SECTOR_SIZE; + offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) + + le32_to_cpu(part->header.BAMOffset)); + +#ifdef PSYCHO_DEBUG + ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&old_addr); + if (ret) { + printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); + return ret; + } + old_addr = le32_to_cpu(old_addr); + + if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) || + ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) || + (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) { + static int ne = 0; + if (++ne < 5) { + printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n"); + printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x" + ", new = 0x%x\n", log_addr, old_addr, virt_addr); + } + return -EIO; + } +#endif + if (part->bam_index == eun) { +#ifdef PSYCHO_DEBUG + if (le32_to_cpu(part->bam_cache[blk]) != old_addr) { + static int ne = 0; + if (++ne < 5) { + printk(KERN_NOTICE "ftl_cs: set_bam_entry() " + "inconsistency!\n"); + printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache" + " = 0x%x\n", + le32_to_cpu(part->bam_cache[blk]), old_addr); + } + return -EIO; + } +#endif + part->bam_cache[blk] = cpu_to_le32(virt_addr); + } + ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&part->bam_cache[blk]); + + if (ret) { + printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n"); + printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n", + log_addr, virt_addr); + } + return ret; +} /* set_bam_entry */ + +static int ftl_write(partition_t *part, caddr_t buffer, + u_long sector, u_long nblocks) +{ + u_int32_t bsize, log_addr, virt_addr, old_addr, blk; + u_long i; + int ret; + size_t retlen, offset; + + DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n", + part, sector, nblocks); + if (!(part->state & FTL_FORMATTED)) { + printk(KERN_NOTICE "ftl_cs: bad partition\n"); + return -EIO; + } + /* See if we need to reclaim space, before we start */ + while (part->FreeTotal < nblocks) { + ret = reclaim_block(part); + if (ret) + return ret; + } + + bsize = 1 << part->header.EraseUnitSize; + + virt_addr = sector * SECTOR_SIZE | BLOCK_DATA; + for (i = 0; i < nblocks; i++) { + if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) { + printk(KERN_NOTICE "ftl_cs: bad write offset\n"); + return -EIO; + } + + /* Grab a free block */ + blk = find_free(part); + if (blk == 0) { + static int ne = 0; + if (++ne < 5) + printk(KERN_NOTICE "ftl_cs: internal error: " + "no free blocks!\n"); + return -ENOSPC; + } + + /* Tag the BAM entry, and write the new block */ + log_addr = part->bam_index * bsize + blk * SECTOR_SIZE; + part->EUNInfo[part->bam_index].Free--; + part->FreeTotal--; + if (set_bam_entry(part, log_addr, 0xfffffffe)) + return -EIO; + part->EUNInfo[part->bam_index].Deleted++; + offset = (part->EUNInfo[part->bam_index].Offset + + blk * SECTOR_SIZE); + ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, + buffer); + + if (ret) { + printk(KERN_NOTICE "ftl_cs: block write failed!\n"); + printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr" + " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr, + offset); + return -EIO; + } + + /* Only delete the old entry when the new entry is ready */ + old_addr = part->VirtualBlockMap[sector+i]; + if (old_addr != 0xffffffff) { + part->VirtualBlockMap[sector+i] = 0xffffffff; + part->EUNInfo[old_addr/bsize].Deleted++; + if (set_bam_entry(part, old_addr, 0)) + return -EIO; + } + + /* Finally, set up the new pointers */ + if (set_bam_entry(part, log_addr, virt_addr)) + return -EIO; + part->VirtualBlockMap[sector+i] = log_addr; + part->EUNInfo[part->bam_index].Deleted--; + + buffer += SECTOR_SIZE; + virt_addr += SECTOR_SIZE; + } + return 0; +} /* ftl_write */ + +/*====================================================================== + + IOCTL calls for getting device parameters. + +======================================================================*/ + +static int ftl_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) +{ + struct hd_geometry *geo = (struct hd_geometry *)arg; + int ret = 0, minor = MINOR(inode->i_rdev); + partition_t *part= myparts[minor >> 4]; + u_long sect; + + if (!part) + return -ENODEV; /* How? */ + + switch (cmd) { + case HDIO_GETGEO: + ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); + if (ret) return ret; + /* Sort of arbitrary: round size down to 4K boundary */ + sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; + put_user(1, (char *)&geo->heads); + put_user(8, (char *)&geo->sectors); + put_user((sect>>3), (short *)&geo->cylinders); + put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); + break; + case BLKGETSIZE: + ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(long)); + if (ret) return ret; + put_user(ftl_hd[minor].nr_sects, + (long *)arg); + break; + case BLKRRPART: + ret = ftl_reread_partitions(minor); + break; +#if (LINUX_VERSION_CODE < 0x20303) + case BLKFLSBUF: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (!capable(CAP_SYS_ADMIN)) return -EACCES; +#endif + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + break; + RO_IOCTLS(inode->i_rdev, arg); +#else + case BLKROSET: + case BLKROGET: + case BLKFLSBUF: + ret = blk_ioctl(inode->i_rdev, cmd, arg); + break; +#endif + default: + ret = -EINVAL; + } + + return ret; +} /* ftl_ioctl */ + +/*====================================================================== + + Handler for block device requests + +======================================================================*/ + +static int ftl_reread_partitions(int minor) +{ + partition_t *part = myparts[minor >> 4]; + int i, whole; + + DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); + if ((atomic_read(&part->open) > 1)) { + return -EBUSY; + } + whole = minor & ~(MAX_PART-1); + + for (i = 0; i < MAX_PART; i++) { + if (ftl_hd[whole+i].nr_sects > 0) { + kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); + sync_dev(rdev); + invalidate_buffers(rdev); + } + ftl_hd[whole+i].start_sect = 0; + ftl_hd[whole+i].nr_sects = 0; + } + + scan_header(part); + + register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, + &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); + +#ifdef PCMCIA_DEBUG + for (i = 0; i < MAX_PART; i++) { + if (ftl_hd[whole+i].nr_sects > 0) + printk(KERN_INFO " %d: start %ld size %ld\n", i, + ftl_hd[whole+i].start_sect, + ftl_hd[whole+i].nr_sects); + } +#endif + return 0; +} + +/*====================================================================== + + Handler for block device requests + +======================================================================*/ + +static void do_ftl_request(request_arg_t) +{ + int ret, minor; + partition_t *part; + + do { + // sti(); + INIT_REQUEST; + + minor = MINOR(CURRENT->rq_dev); + + part = myparts[minor >> 4]; + if (part) { + ret = 0; + + switch (CURRENT->cmd) { + case READ: + ret = ftl_read(part, CURRENT->buffer, + CURRENT->sector+ftl_hd[minor].start_sect, + CURRENT->current_nr_sectors); + if (ret) printk("ftl_read returned %d\n", ret); + break; + + case WRITE: + ret = ftl_write(part, CURRENT->buffer, + CURRENT->sector+ftl_hd[minor].start_sect, + CURRENT->current_nr_sectors); + if (ret) printk("ftl_write returned %d\n", ret); + break; + + default: + panic("ftl_cs: unknown block command!\n"); + + } + } else { + ret = 1; + printk("NULL part in ftl_request\n"); + } + + if (!ret) { + CURRENT->sector += CURRENT->current_nr_sectors; + } + + end_request((ret == 0) ? 1 : 0); + } while (1); +} /* do_ftl_request */ + +/*====================================================================*/ + +void ftl_freepart(partition_t *part) +{ + if (part->VirtualBlockMap) { + vfree(part->VirtualBlockMap); + part->VirtualBlockMap = NULL; + } + if (part->VirtualPageMap) { + kfree(part->VirtualPageMap); + part->VirtualPageMap = NULL; + } + if (part->EUNInfo) { + kfree(part->EUNInfo); + part->EUNInfo = NULL; + } + if (part->XferInfo) { + kfree(part->XferInfo); + part->XferInfo = NULL; + } + if (part->bam_cache) { + kfree(part->bam_cache); + part->bam_cache = NULL; + } + +} /* ftl_freepart */ + +static void ftl_notify_add(struct mtd_info *mtd) +{ + partition_t *partition; + int device; + + for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) + ; + + if (device == MAX_MTD_DEVICES) { + printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" + "Not scanning <%s>\n", mtd->name); + return; + } + + partition = kmalloc(sizeof(partition_t), GFP_KERNEL); + + if (!partition) { + printk(KERN_WARNING "No memory to scan for FTL on %s\n", + mtd->name); + return; + } + + memset(partition, 0, sizeof(partition_t)); + + partition->mtd = mtd; + + if ((scan_header(partition) == 0) && + (build_maps(partition) == 0)) { + + partition->state = FTL_FORMATTED; + atomic_set(&partition->open, 0); + myparts[device] = partition; + ftl_reread_partitions(device << 4); +#ifdef PCMCIA_DEBUG + printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", + le32_to_cpu(partition->header.FormattedSize) >> 10); +#endif + } +} + +static void ftl_notify_remove(struct mtd_info *mtd) +{ + int i,j; + + /* Q: What happens if you try to remove a device which has + * a currently-open FTL partition on it? + * + * A: You don't. The ftl_open routine is responsible for + * increasing the use count of the driver module which + * it uses. + */ + + /* That's the theory, anyway :) */ + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (myparts[i] && myparts[i]->mtd == mtd) { + + if (myparts[i]->state == FTL_FORMATTED) + ftl_freepart(myparts[i]); + + myparts[i]->state = 0; + for (j=0; j<16; j++) { + ftl_gendisk.part[j].nr_sects=0; + ftl_gendisk.part[j].start_sect=0; + } + kfree(myparts[i]); + myparts[i] = NULL; + } +} + + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_ftl init_module +#define cleanup_ftl cleanup_module +#endif +#endif + +mod_init_t init_ftl(void) +{ + int i; + + memset(myparts, 0, sizeof(myparts)); + + DEBUG(0, "$Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $\n"); + + if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { + printk(KERN_NOTICE "ftl_cs: unable to grab major " + "device number!\n"); + return -EAGAIN; + } + + for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) + ftl_blocksizes[i] = 1024; + for (i = 0; i < MAX_DEV*MAX_PART; i++) { + ftl_hd[i].nr_sects = 0; + ftl_hd[i].start_sect = 0; + } + blksize_size[FTL_MAJOR] = ftl_blocksizes; + ftl_gendisk.major = FTL_MAJOR; + blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); + ftl_gendisk.next = gendisk_head; + gendisk_head = &ftl_gendisk; + + register_mtd_user(&ftl_notifier); + + return 0; +} + +mod_exit_t cleanup_ftl(void) +{ + struct gendisk *gd, **gdp; + + unregister_mtd_user(&ftl_notifier); + + unregister_blkdev(FTL_MAJOR, "ftl"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); + blksize_size[FTL_MAJOR] = NULL; + + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &ftl_gendisk) { + gd = *gdp; *gdp = gd->next; + break; + } +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_ftl); +module_exit(cleanup_ftl); +#endif diff --git a/drivers/mtd/jedec.c b/drivers/mtd/jedec.c new file mode 100644 index 000000000..74e9db84e --- /dev/null +++ b/drivers/mtd/jedec.c @@ -0,0 +1,773 @@ + +/* JEDEC Flash Interface. + * This is an older type of interface for self programming flash. It is + * commonly use in older AMD chips and is obsolete compared with CFI. + * It is called JEDEC because the JEDEC association distributes the ID codes + * for the chips. + * + * See the AMD flash databook for information on how to operate the interface. + * + * This code does not support anything wider than 8 bit flash chips, I am + * not going to guess how to send commands to them, plus I expect they will + * all speak CFI.. + * + * $Id: jedec.c,v 1.1 2000/07/04 07:21:57 jgg Exp $ + */ + +#include <linux/mtd/jedec.h> + +struct mtd_info *jedec_probe(struct map_info *); +int jedec_probe8(struct map_info *map,unsigned long base, + struct jedec_private *priv); +int jedec_probe16(struct map_info *map,unsigned long base, + struct jedec_private *priv); +int jedec_probe32(struct map_info *map,unsigned long base, + struct jedec_private *priv); +static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start, + unsigned long len); +static int flash_erase(struct mtd_info *mtd, struct erase_info *instr); +static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u_char *buf); + +EXPORT_SYMBOL(jedec_probe); + +/* Listing of parts and sizes. We need this table to learn the sector + size of the chip and the total length */ +static const struct JEDECTable JEDEC_table[] = + {{0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH}, + {}}; + +static void jedec_sync(struct mtd_info *mtd) {}; +static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); + +/* Probe entry point */ + struct jedec_private priv; + struct mtd_info __MTD; +struct mtd_info *jedec_probe(struct map_info *map) +{ + struct mtd_info *MTD = &__MTD; + unsigned long Base; + unsigned long SectorSize; + unsigned count; + unsigned I,Uniq; + char Part[200]; + memset(&priv,0,sizeof(priv)); + + if (map->bank_size == 0) + map->bank_size = map->size; + + if (map->size/map->bank_size > MAX_JEDEC_CHIPS) + { + printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n"); + return 0; + } + + for (Base = 0; Base < map->size; Base += map->bank_size) + { + // Perhaps zero could designate all tests? + if (map->bus_width == 0) + map->bus_width = 8; + + if (map->bus_width == 8) + jedec_probe8(map,Base,&priv); + if (map->bus_width == 16) + jedec_probe16(map,Base,&priv); + if (map->bus_width == 32) + jedec_probe32(map,Base,&priv); + } + + // Get the biggest sector size + SectorSize = 0; + for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + if (priv.chips[I].sectorsize > SectorSize) + SectorSize = priv.chips[I].sectorsize; + } + + // Quickly ensure that the other sector sizes are factors of the largest + for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + if ((SectorSize/priv.chips[I].sectorsize)*priv.chips[I].sectorsize != SectorSize) + { + printk("mtd: Failed. Device has incompatible mixed sector sizes\n"); + return 0; + } + } + + /* Generate a part name that includes the number of different chips and + other configuration information */ + count = 1; + strncpy(Part,map->name,sizeof(Part)-10); + Part[sizeof(Part)-11] = 0; + strcat(Part," "); + Uniq = 0; + for (I = 0; priv.chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + const struct JEDECTable *JEDEC; + + if (priv.chips[I+1].jedec == priv.chips[I].jedec) + { + count++; + continue; + } + + // Locate the chip in the jedec table + JEDEC = jedec_idtoinf(priv.chips[I].jedec >> 8,priv.chips[I].jedec); + if (JEDEC == 0) + { + printk("mtd: Internal Error, JEDEC not set\n"); + return 0; + } + + if (Uniq != 0) + strcat(Part,","); + Uniq++; + + if (count != 1) + sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name); + else + sprintf(Part+strlen(Part),"%s",JEDEC->name); + if (strlen(Part) > sizeof(Part)*2/3) + break; + count = 1; + } + + /* Determine if the chips are organized in a linear fashion, or if there + are empty banks. Note, the last bank does not count here, only the + first banks are important. Holes on non-bank boundaries can not exist + due to the way the detection algorithm works. */ + if (priv.size < map->bank_size) + map->bank_size = priv.size; + priv.is_banked = 0; + for (I = 0; I != priv.size/map->bank_size - 1; I++) + { + if (priv.bank_fill[I] != map->bank_size) + priv.is_banked = 1; + + /* This even could be eliminated, but new de-optimized read/write + functions have to be written */ + if (priv.bank_fill[I] != priv.bank_fill[0]) + { + printk("mtd: Failed. Cannot handle unsymetric banking\n"); + return 0; + } + } + if (priv.is_banked == 1) + strcat(Part,", banked"); + + xprintf("Part: '%s'\n",Part); + + memset(MTD,0,sizeof(*MTD)); + strncpy(MTD->name,Part,sizeof(MTD->name)); + MTD->name[sizeof(MTD->name)-1] = 0; + MTD->type = MTD_NORFLASH; + MTD->flags = MTD_CAP_NORFLASH; + MTD->erasesize = SectorSize*(map->bus_width/8); + MTD->size = priv.size; + //MTD->module = THIS_MODULE; // ? Maybe this should be the low level module? + MTD->erase = flash_erase; + if (priv.is_banked == 1) + MTD->read = jedec_read_banked; + else + MTD->read = jedec_read; + MTD->write = flash_write; + MTD->sync = jedec_sync; + MTD->priv = map; + map->fldrv_priv = &priv; + + return MTD; +} + +/* Helper for the JEDEC function, JEDEC numbers all have odd parity */ +static int checkparity(u_char C) +{ + u_char parity = 0; + while (C != 0) + { + parity ^= C & 1; + C >>= 1; + } + + return parity == 1; +} + + +/* Take an array of JEDEC numbers that represent interleved flash chips + and process them. Check to make sure they are good JEDEC numbers, look + them up and then add them to the chip list */ +int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count, + unsigned long base,struct jedec_private *priv) +{ + unsigned I,J; + unsigned long Size; + unsigned long SectorSize; + const struct JEDECTable *JEDEC; + + // Test #2 JEDEC numbers exhibit odd parity + for (I = 0; I != Count; I++) + { + if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0) + return 0; + } + + // Finally, just make sure all the chip sizes are the same + JEDEC = jedec_idtoinf(Mfg[0],Id[0]); + + if (JEDEC == 0) + { + printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]); + return 0; + } + + Size = JEDEC->size; + SectorSize = JEDEC->sectorsize; + for (I = 0; I != Count; I++) + { + JEDEC = jedec_idtoinf(Mfg[0],Id[0]); + if (JEDEC == 0) + { + printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]); + return 0; + } + + if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize) + { + printk("mtd: Failed. Interleved flash does not have matching characteristics\n"); + return 0; + } + } + + // Load the Chips + for (I = 0; I != MAX_JEDEC_CHIPS; I++) + { + if (priv->chips[I].jedec == 0) + break; + } + + if (I + Count > MAX_JEDEC_CHIPS) + { + printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n"); + return 0; + } + + // Add them to the table + for (J = 0; J != Count; J++) + { + unsigned long Bank; + + JEDEC = jedec_idtoinf(Mfg[J],Id[J]); + priv->chips[I].jedec = (Mfg[J] << 8) | Id[J]; + priv->chips[I].size = JEDEC->size; + priv->chips[I].sectorsize = JEDEC->sectorsize; + priv->chips[I].base = base + J; + priv->chips[I].datashift = J*8; + priv->chips[I].capabilities = JEDEC->capabilities; + priv->chips[I].offset = priv->size + J; + + // log2 n :| + priv->chips[I].addrshift = 0; + for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++); + + // Determine how filled this bank is. + Bank = base & (~(map->bank_size-1)); + if (priv->bank_fill[Bank/map->bank_size] < base + + (JEDEC->size << priv->chips[I].addrshift) - Bank) + priv->bank_fill[Bank/map->bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank; + I++; + } + + priv->size += priv->chips[I-1].size*Count; + + return priv->chips[I-1].size; +} + +/* Lookup the chip information from the JEDEC ID table. */ +const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id) +{ + __u16 Id = (mfr << 8) | id; + unsigned long I = 0; + for (I = 0; JEDEC_table[I].jedec != 0; I++) + if (JEDEC_table[I].jedec == Id) + return JEDEC_table + I; + return 0; +} + +// Look for flash using an 8 bit bus interface +int jedec_probe8(struct map_info *map,unsigned long base, + struct jedec_private *priv) +{ + return 0; +} + +// Look for flash using a 16 bit bus interface (ie 2 8-bit chips) +int jedec_probe16(struct map_info *map,unsigned long base, + struct jedec_private *priv) +{ + return 0; +} + +// Look for flash using a 32 bit bus interface (ie 4 8-bit chips) +int jedec_probe32(struct map_info *map,unsigned long base, + struct jedec_private *priv) +{ + #define flread(x) map->read32(map,base+((x)<<2)) + #define flwrite(v,x) map->write32(map,v,base+((x)<<2)) + + const unsigned long AutoSel1 = 0xAAAAAAAA; + const unsigned long AutoSel2 = 0x55555555; + const unsigned long AutoSel3 = 0x90909090; + const unsigned long Reset = 0x90909090; + __u32 OldVal; + __u8 Mfg[4]; + __u8 Id[4]; + unsigned I; + unsigned long Size; + + // Wait for any write/erase operation to settle + OldVal = flread(base); + for (I = 0; OldVal != flread(base) && I < 10000; I++) + OldVal = flread(base); + + // Reset the chip + flwrite(Reset,0x555); + + // Send the sequence + flwrite(AutoSel1,0x555); + flwrite(AutoSel2,0x2AA); + flwrite(AutoSel3,0x555); + + // Test #1, JEDEC numbers are readable from 0x??00/0x??01 + if (flread(0) != flread(0x100) || + flread(1) != flread(0x101)) + { + flwrite(Reset,0x555); + return 0; + } + + // Split up the JEDEC numbers + OldVal = flread(0); + for (I = 0; I != 4; I++) + Mfg[I] = (OldVal >> (I*8)); + OldVal = flread(1); + for (I = 0; I != 4; I++) + Id[I] = (OldVal >> (I*8)); + + Size = handle_jedecs(map,Mfg,Id,4,base,priv); + if (Size == 0) + { + flwrite(Reset,0x555); + return 0; + } + + /* Check if there is address wrap around within a single bank, if this + returns JEDEC numbers then we assume that it is wrap around. Notice + we call this routine with the JEDEC return still enabled, if two or + more flashes have a truncated address space the probe test will still + work */ + if (base + Size+0x555 < map->size && + base + Size+0x555 < (base & (~(map->bank_size-1))) + map->bank_size) + { + if (flread(base+Size) != flread(base+Size + 0x100) || + flread(base+Size + 1) != flread(base+Size + 0x101)) + { + jedec_probe32(map,base+Size,priv); + } + } + + // Reset. + flwrite(0xF0F0F0F0,0x555); + + return 1; + + #undef flread + #undef flwrite +} + +/* Linear read. */ +static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; + + map->copy_from(map, buf, from, len); + *retlen = len; + return 0; +} + +/* Banked read. Take special care to jump past the holes in the bank + mapping. This version assumes symetry in the holes.. */ +static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; + struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + + *retlen = 0; + while (len > 0) + { + // Determine what bank and offset into that bank the first byte is + unsigned long bank = from & (~(priv->bank_fill[0]-1)); + unsigned long offset = from & (priv->bank_fill[0]-1); + unsigned long get = len; + if (priv->bank_fill[0] - offset < len) + get = priv->bank_fill[0] - offset; + + bank /= priv->bank_fill[0]; + map->copy_from(map,buf + *retlen,bank*map->bank_size + offset,get); + + len -= get; + *retlen += get; + from += get; + } + return 0; +} + +/* Pass the flags value that the flash return before it re-entered read + mode. */ +static void jedec_flash_failed(unsigned char code) +{ + /* Bit 5 being high indicates that there was an internal device + failure, erasure time limits exceeded or something */ + if ((code & (1 << 5)) != 0) + { + printk("mtd: Internal Flash failure\n"); + return; + } + printk("mtd: Programming didn't take\n"); +} + +/* This uses the erasure function described in the AMD Flash Handbook, + it will work for flashes with a fixed sector size only. Flashes with + a selection of sector sizes (ie the AMD Am29F800B) will need a different + routine. This routine tries to parallize erasing multiple chips/sectors + where possible */ +static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + // Does IO to the currently selected chip + #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift)) + #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift)) + + unsigned long Time = 0; + unsigned long NoTime = 0; + unsigned long start = instr->addr, len = instr->len; + unsigned int I; + struct map_info *map = (struct map_info *)mtd->priv; + struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + + // Verify the arguments.. + if (start + len > mtd->size || + (start % mtd->erasesize) != 0 || + (len % mtd->erasesize) != 0 || + (len/mtd->erasesize) == 0) + return -EINVAL; + + jedec_flash_chip_scan(priv,start,len); + + // Start the erase sequence on each chip + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + unsigned long off; + struct jedec_flash_chip *chip = priv->chips + I; + + if (chip->length == 0) + continue; + + // Send the erase setup code + xprintf("Erase: "); + puth(chip->start); putc(' '); + puth(chip->base); putc(' '); + puth(chip->length); putc(' '); + puth(chip->sectorsize); putc('\n'); + + if (chip->start + chip->length > chip->size) + { + xprintf("DIE\n"); + return -EIO; + } + + flwrite(0xF0,chip->start + 0x555); + flwrite(0xAA,chip->start + 0x555); + flwrite(0x55,chip->start + 0x2AA); + flwrite(0x80,chip->start + 0x555); + flwrite(0xAA,chip->start + 0x555); + flwrite(0x55,chip->start + 0x2AA); + + // Use chip erase if possible + if (chip->start == 0 && chip->length == chip->size) + { + flwrite(0x10,0x555); + continue; + } + + /* Once we start selecting the erase sectors the delay between each + command must not exceed 50us or it will immediately start erasing + and ignore the other sectors */ +/* how do you portably turn off interrupts? + save_flags(flags); + cli();*/ + for (off = 0; off < chip->length; off += chip->sectorsize) + { + // Check to make sure we didn't timeout + flwrite(0x30,chip->start + off); + if (off == 0) + continue; + if ((flread(chip->start + off) & (1 << 3)) != 0) + { + printk("mtd: Ack! We timed out the erase timer!\n"); + return -EIO; + } + } +// restore_flags(flags); + } + + /* We could split this into a timer routine and return early, performing + background erasure.. Maybe later if the need warrents */ + + /* Poll the flash for erasure completion, specs say this can take as long + as 480 seconds to do all the sectors (for a 2 meg flash). + Erasure time is dependant on chip age, temp and wear.. */ + + /* This being a generic routine assumes a 32 bit bus. It does read32s + and bundles interleved chips into the same grouping. This will work + for all bus widths */ + Time = 0; + NoTime = 0; + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + struct jedec_flash_chip *chip = priv->chips + I; + unsigned long off = 0; + unsigned todo[4] = {0,0,0,0}; + unsigned todo_left = 0; + unsigned J; + + if (chip->length == 0) + continue; + + /* Find all chips in this data line, realistically this is all + or nothing up to the interleve count */ + for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++) + { + if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) == + (chip->base & (~((1<<chip->addrshift)-1)))) + { + todo_left++; + todo[priv->chips[J].base & ((1<<chip->addrshift)-1)] = 1; + } + } + + xprintf("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1], + (short)todo[2],(short)todo[3]); + + while (1) + { + __u32 Last[4]; + unsigned long Count = 0; + + /* During erase bit 7 is held low and bit 6 toggles, we watch this, + should it stop toggling or go high then the erase is completed, + or this is not really flash ;> */ + Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Count = 3; + while (todo_left != 0) + { + for (J = 0; J != 4; J++) + { + __u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF; + __u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF; + __u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF; + if (todo[J] == 0) + continue; + + if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2) + { +// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2); + continue; + } + + if (Byte1 == Byte2) + { + jedec_flash_failed(Byte3); + return -EIO; + } + + todo[J] = 0; + todo_left--; + } + +/* if (NoTime == 0) + Time += HZ/10 - schedule_timeout(HZ/10);*/ + NoTime = 0; + + Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); + Count++; + + putc('.'); + +/* // Count time, max of 15s per sector (according to AMD) + if (Time > 15*len/mtd->erasesize*HZ) + { + printk("mtd: Flash Erase Timed out\n"); + return -EIO; + } */ + } + + puts("out\n"); + + // Skip to the next chip if we used chip erase + if (chip->length == chip->size) + off = chip->size; + else + off += chip->sectorsize; + + if (off >= chip->length) + break; + NoTime = 1; + } + + for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++) + { + if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) == + (chip->base & (~((1<<chip->addrshift)-1)))) + priv->chips[J].length = 0; + } + } + + puts("done\n"); + return 0; + + #undef flread + #undef flwrite +} + +/* This is the simple flash writing function. It writes to every byte, in + sequence. It takes care of how to properly address the flash if + the flash is interleved. It can only be used if all the chips in the + array are identical!*/ +static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u_char *buf) +{ + /* Does IO to the currently selected chip. It takes the bank addressing + base (which is divisable by the chip size) adds the necesary lower bits + of addrshift (interleve index) and then adds the control register index. */ + #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) + #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) + + struct map_info *map = (struct map_info *)mtd->priv; + struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + unsigned long base; + unsigned long off; + + if (start + len > mtd->size) + return -EIO; + + puts("Here"); + + while (len != 0) + { + struct jedec_flash_chip *chip = priv->chips; + unsigned long bank; + unsigned long boffset; + + // Compute the base of the flash. + off = start % (chip->size << chip->addrshift); + base = start - off; + + // Perform banked addressing translation. + bank = base & (~(priv->bank_fill[0]-1)); + boffset = base & (priv->bank_fill[0]-1); + bank = (bank/priv->bank_fill[0])*map->bank_size; + base = bank + boffset; + + xprintf("Flasing %X %X %X\n",base,chip->size,len); + + // Loop over this page + for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) + { + unsigned char oldbyte = map->read8(map,base+off); + unsigned char Last[4]; + unsigned long Count = 0; + +// putc('.'); + + if (oldbyte == *buf) + continue; + if (((~oldbyte) & *buf) != 0) + printk("mtd: warn: Trying to set a 0 to a 1\n"); + + // Write + flwrite(0xAA,0x555); + flwrite(0x55,0x2AA); + flwrite(0xA0,0x555); + map->write8(map,*buf,base + off); + Last[0] = map->read8(map,base + off); + Last[1] = map->read8(map,base + off); + Last[2] = map->read8(map,base + off); + + /* Wait for the flash to finish the operation. We store the last 4 + status bytes that have been retrieved so we can determine why + it failed. The toggle bits keep toggling when there is a + failure */ + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && + Count < 10000; Count++) + Last[Count % 4] = map->read8(map,base + off); + if (Last[(Count - 1) % 4] != *buf) + { + jedec_flash_failed(Last[(Count - 3) % 4]); + return -EIO; + } + } + } + *retlen = len; + return 0; +} + +/* This is used to enhance the speed of the erase routine, + when things are being done to multiple chips it is possible to + parallize the operations, particularly full memory erases of multi + chip memories benifit */ +static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start, + unsigned long len) +{ + unsigned int I; + + // Zero the records + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + priv->chips[I].start = priv->chips[I].length = 0; + + // Intersect the region with each chip + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) + { + struct jedec_flash_chip *chip = priv->chips + I; + unsigned long ByteStart; + unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift); + + // End is before this chip or the start is after it + if (start+len < chip->offset || + ChipEndByte - (1 << chip->addrshift) < start) + continue; + + if (start < chip->offset) + { + ByteStart = chip->offset; + chip->start = 0; + } + else + { + chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift; + ByteStart = start; + } + + if (start + len >= ChipEndByte) + chip->length = (ChipEndByte - ByteStart) >> chip->addrshift; + else + chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift; + } +} + /*}}}*/ diff --git a/drivers/mtd/map_ram.c b/drivers/mtd/map_ram.c new file mode 100644 index 000000000..706f7f5ff --- /dev/null +++ b/drivers/mtd/map_ram.c @@ -0,0 +1,110 @@ +/* + * Common code to handle map devices which are simple RAM + * (C) 2000 Red Hat. GPL'd. + * $Id: map_ram.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +#include <linux/mtd/map.h> + + +static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int mapram_erase (struct mtd_info *, struct erase_info *); +static void mapram_nop (struct mtd_info *); + +struct mtd_info *map_ram_probe(struct map_info *); +EXPORT_SYMBOL(map_ram_probe); + +struct mtd_info *map_ram_probe(struct map_info *map) +{ + struct mtd_info *mtd; + + /* Check the first byte is RAM */ + map->write8(map, 0x55, 0); + if (map->read8(map, 0) != 0x55) + return NULL; + + map->write8(map, 0xAA, 0); + if (map->read8(map, 0) != 0xAA) + return NULL; + + /* Check the last byte is RAM */ + map->write8(map, 0x55, map->size-1); + if (map->read8(map, map->size-1) != 0x55) + return NULL; + + map->write8(map, 0xAA, map->size-1); + if (map->read8(map, map->size-1) != 0xAA) + return NULL; + + /* OK. It seems to be RAM. */ + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return NULL; + + memset(mtd, 0, sizeof(*mtd)); + + map->fldrv_destroy = mapram_nop; + mtd->priv = map; + mtd->name = map->name; + mtd->type = MTD_RAM; + mtd->erasesize = 0x10000; + mtd->size = map->size; + mtd->erase = mapram_erase; + mtd->read = mapram_read; + mtd->write = mapram_write; + mtd->sync = mapram_nop; + mtd->flags = MTD_CAP_RAM | MTD_VOLATILE; + + MOD_INC_USE_COUNT; + return mtd; +} + + +static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; + + map->copy_from(map, buf, from, len); + *retlen = len; + return 0; +} + +static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; + + map->copy_to(map, to, buf, len); + *retlen = len; + return 0; +} + +static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + /* Yeah, it's inefficient. Who cares? It's faster than a _real_ + flash erase. */ + struct map_info *map = (struct map_info *)mtd->priv; + unsigned long i; + + for (i=0; i<instr->len; i++) + map->write8(map, 0xFF, instr->addr + i); + + if (instr->callback) + instr->callback(instr); + + return 0; +} + +static void mapram_nop(struct mtd_info *mtd) +{ + /* Nothing to see here */ +} diff --git a/drivers/mtd/map_rom.c b/drivers/mtd/map_rom.c new file mode 100644 index 000000000..d353938e9 --- /dev/null +++ b/drivers/mtd/map_rom.c @@ -0,0 +1,60 @@ +/* + * Common code to handle map devices which are simple ROM + * (C) 2000 Red Hat. GPL'd. + * $Id: map_rom.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <asm/byteorder.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +#include <linux/mtd/map.h> + + +static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static void maprom_nop (struct mtd_info *); + +struct mtd_info *map_rom_probe(struct map_info *); +EXPORT_SYMBOL(map_rom_probe); + +struct mtd_info *map_rom_probe(struct map_info *map) +{ + struct mtd_info *mtd; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + return NULL; + + memset(mtd, 0, sizeof(*mtd)); + + map->fldrv_destroy = maprom_nop; + mtd->priv = map; + mtd->name = map->name; + mtd->type = MTD_ROM; + mtd->size = map->size; + mtd->read = maprom_read; + mtd->sync = maprom_nop; + mtd->flags = MTD_CAP_ROM; + + MOD_INC_USE_COUNT; + return mtd; +} + + +static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = (struct map_info *)mtd->priv; + + map->copy_from(map, buf, from, len); + *retlen = len; + return 0; +} + +static void maprom_nop(struct mtd_info *mtd) +{ + /* Nothing to see here */ +} diff --git a/drivers/mtd/mapped.c b/drivers/mtd/mapped.c new file mode 100644 index 000000000..84a74036e --- /dev/null +++ b/drivers/mtd/mapped.c @@ -0,0 +1,674 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: mapped.c,v 1.8 2000/03/31 14:40:42 dwmw2 Exp $ +/* ###################################################################### + + Flash MTD Routines + + These routine support IDing and manipulating flash. Currently the + older JEDEC ID mechanism and a table is used for determining the + flash characterisitics, but it is trivial to add support for the + CFI specification: + http://www.pentium.com/design/flash/ in the technote section. + + ##################################################################### */ + /*}}}*/ +#include <linux/mtd/mapped.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <asm/io.h> +#include <asm/delay.h> + +struct JEDECTable mtd_JEDEC_table[] = + {{0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, + {}}; + +// flash_setup - Setup the mapped_mtd_info structure for normal flash /*{{{*/ +// --------------------------------------------------------------------- +/* There is a set of commands that flash manufactures follow for getting the + JEDEC id, erasing and writing. So long as your flash device supports + getting the JEDEC ID in this (standard?) way it will be supported as flash, + otherwise it is converted to ROM. Upon completion the structure is + registered with the MTD layer */ +int mtd_mapped_setup(struct mapped_mtd_info *map) +{ + DEBUG(1, "\n"); + // Must define a page function to use the defaults! + if (map->page == 0) + return -1; + + if (map->jedec_sense == 0) + map->jedec_sense = flash_jedec; + + if (map->jedec_sense(map) != 0) + return -1; + + if (map->mtd.erase == 0 && map->mtd.type == MTD_NORFLASH) + map->mtd.erase = flash_erase; + if (map->mtd.write == 0) + { + if (map->mtd.type == MTD_NORFLASH) + map->mtd.write = flash_write; + if (map->mtd.type == MTD_RAM) + map->mtd.write = ram_write; + } + if (map->mtd.read == 0) + map->mtd.read = rom_read; + + return add_mtd_device(&map->mtd); +} + /*}}}*/ +// flash_remove - Remove the flash device from the MTD layer /*{{{*/ +// --------------------------------------------------------------------- +/* Free any memory allocated for the device here */ +int mtd_mapped_remove(struct mapped_mtd_info *map) +{ + return del_mtd_device(&map->mtd); +} + /*}}}*/ + +// checkparity - Checks a number for odd parity /*{{{*/ +// --------------------------------------------------------------------- +/* Helper for the JEDEC function, JEDEC numbers all have odd parity */ +static int checkparity(u_char C) +{ + u_char parity = 0; + while (C != 0) + { + parity ^= C & 1; + C >>= 1; + } + + return parity == 1; +} + /*}}}*/ +// SetJedec - Set the jedec information for a chip /*{{{*/ +// --------------------------------------------------------------------- +/* We track the configuration of each chip separately in the chip list, + each chip can have a different type and configuration to allow for + maximum flexability. */ +void set_jedec(struct mapped_mtd_info *map,unsigned chip,unsigned char mfr, + unsigned char id) +{ + unsigned long longID = (mfr << 8) + id; + unsigned int I; + + map->mtd.type = MTD_NORFLASH; + map->mfr = mfr; + map->id = id; + + // Locate the chip in the jedec table + for (I = 0; mtd_JEDEC_table[I].jedec != 0; I++) + { + if (mtd_JEDEC_table[I].jedec == longID) + break; + } + + if (mtd_JEDEC_table[I].jedec != longID || longID == 0) + { + printk("Unknown JEDEC number %x-%x, treating as ROM\n",map->mfr, + map->id); + map->mtd.type = MTD_ROM; + return; + } + + // Setup the MTD from the JEDEC information +// map->mtd.size = mtd_JEDEC_table[I].size; +// map->mtd.erasesize = mtd_JEDEC_table[I].sectorsize; +// map->mtd.capabilities = mtd_JEDEC_table[I].capabilities; +// strncpy(map->mtd.part,mtd_JEDEC_table[I].name,sizeof(map->mtd.part)-1); + + map->chips[chip].jedec = longID; + map->chips[chip].size = mtd_JEDEC_table[I].size; + map->chips[chip].sectorsize = mtd_JEDEC_table[I].sectorsize; + map->chips[chip].capabilities = mtd_JEDEC_table[I].capabilities; + map->chips[chip].base = 0; +} + /*}}}*/ +// isjedec - Check if reading from the memory location gives jedec #s /*{{{*/ +// --------------------------------------------------------------------- +/* This is ment to be called on the flash window once it is in jedec mode */ +int isjedec(unsigned long base) +{ + // Test #1, JEDEC numbers are readable from 0x??00/0x??01 + if (readb(base + 0) != readb(base + 0x100) || + readb(base + 1) != readb(base + 0x101)) + return 0; + + // Test #2 JEDEC numbers exhibit odd parity + if (checkparity(readb(base + 0)) == 0 || checkparity(readb(base + 1)) == 0) + return 0; + return 1; +} + /*}}}*/ +// flash_jedec - JEDEC ID sensor /*{{{*/ +// --------------------------------------------------------------------- +/* The mysterious jedec flash probe sequence writes a specific pattern of + bytes to the flash. This should be general enough to work with any MTD + structure that may contain a flash chip, but note that it will corrupt + address 0x5555 on SRAM cards if the machine dies between the two + critical operations. */ +int flash_jedec(struct mapped_mtd_info *map) +{ + unsigned I; + u_char OldVal; + unsigned long base; + unsigned long baseaddr = 0; + unsigned chip = 0; + unsigned count; + + // Who has a page size this small? :> + if (map->pagesize < 0x555) + return 1; + + base = map->page(map,0); + + // Wait for any write/erase operation to settle + OldVal = readb(base); + for (I = 0; OldVal != readb(base) && I < 10000; I++) + OldVal = readb(base); + + /* Check for sram by writing to it, the write also happens to be part + of the flash reset sequence.. */ + OldVal = readb(base + 0x555); + writeb(OldVal,base + 0x555); + writeb(0xF0,base + 0x555); + if (OldVal != readb(base + 0x555)) + { + udelay(100); + + // Set it back and make sure.. + writeb(OldVal,base + 0x555); + if (OldVal == readb(base + 0x555)) + { + map->mtd.type = MTD_RAM; + return 0; + } + + writeb(0xF0,base + 0x555); + } + + // Probe for chips + while (chip < sizeof(map->chips)/sizeof(map->chips[0])) + { + // Already in jedec mode, we might be doing some address wrap around + if (chip != 0 && isjedec(base) != 0) + { + /* Try to reset this page and check if that resets the first page + to confirm */ + writeb(0xF0,base + 0x555); + if (isjedec(base) != 0) + break; + base = map->page(map,0); + if (isjedec(base) == 0) + break; + base = map->page(map,baseaddr/map->pagesize); + } + + // Send the sequence + writeb(0xAA,base + 0x555); + writeb(0x55,base + 0x2AA); + writeb(0x90,base + 0x555); + + // Check the jedec number + if (isjedec(base) == 0) + { + /* If this is the first chip it must be rom, otherwise it is the + end of the flash region */ + if (chip == 0) + { + map->mtd.type = MTD_ROM; + return 0; + } + break; + } + + // Store the jdec info + set_jedec(map,chip,readb(base + 0),readb(base + 1)); + map->chips[chip].base = baseaddr; + + // Jump to the next chip + baseaddr += map->chips[chip].size; + if (baseaddr/map->pagesize > map->maxsize) + break; + base = map->page(map,baseaddr/map->pagesize); + if (base == 0) + return -EIO; + + chip++; + } + + // Reset all of the chips + map->mtd.size = 0; + baseaddr = 0; + map->mtd.flags = 0xFFFF; + for (I = 0; map->chips[I].jedec != 0; I++) + { + // Fill in the various MTD structures + map->mtd.size += map->chips[I].size; + if (map->mtd.erasesize < map->chips[I].sectorsize) + map->mtd.erasesize = map->chips[I].sectorsize; + map->mtd.flags &= map->chips[I].capabilities; + + base = map->page(map,baseaddr/map->pagesize); + baseaddr += map->chips[chip].size; + writeb(0xF0,base + 0); // Reset + } + + /* Generate a part name that includes the number of different chips and + other configuration information */ + count = 1; + map->part[0] = 0; + for (I = 0; map->chips[I].jedec != 0; I++) + { + unsigned J; + if (map->chips[I+1].jedec == map->chips[I].jedec) + { + count++; + continue; + } + + // Locate the chip in the jedec table + for (J = 0; mtd_JEDEC_table[J].jedec != 0; J++) + { + if (mtd_JEDEC_table[J].jedec == map->chips[I].jedec) + break; + } + + if (map->part[0] != 0) + strcat(map->part,","); + + if (count != 1) + sprintf(map->part+strlen(map->part),"%u*[%s]",count, + mtd_JEDEC_table[J].name); + else + sprintf(map->part+strlen(map->part),"%s", + mtd_JEDEC_table[J].name); + count = 1; + } + return 0; +} + /*}}}*/ + +// flash_failed - Print a console message about why the failure /*{{{*/ +// --------------------------------------------------------------------- +/* Pass the flags value that the flash return before it re-entered read + mode. */ +static void flash_failed(unsigned char code) +{ + /* Bit 5 being high indicates that there was an internal device + failure, erasure time limits exceeded or something */ + if ((code & (1 << 5)) != 0) + { + printk("mtd: Internal Flash failure\n"); + return; + } + printk("mtd: Programming didn't take\n"); +} + /*}}}*/ +// flash_erase - Generic erase function /*{{{*/ +// --------------------------------------------------------------------- +/* This uses the erasure function described in the AMD Flash Handbook, + it will work for flashes with a fixed sector size only. Flashes with + a selection of sector sizes (ie the AMD Am29F800B) will need a different + routine. This routine tries to parallize erasing multiple chips/sectors + where possible */ +int flash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + unsigned long Time = 0; + unsigned long NoTime = 0; + unsigned long start = instr->addr, len = instr->len; + unsigned int I; + struct mapped_mtd_info *map = (struct mapped_mtd_info *)mtd; + + // Verify the arguments.. + if (start + len > map->mtd.size || + (start % map->mtd.erasesize) != 0 || + (len % map->mtd.erasesize) != 0 || + (len/map->mtd.erasesize) == 0) + return -EINVAL; + + flash_chip_scan(map,start,len); + + // Start the erase sequence on each chip + for (I = 0; map->chips[I].jedec != 0; I++) + { + unsigned long off; + struct flash_chip *chip = map->chips + I; + unsigned long base; + unsigned long flags; + + if (chip->length == 0) + continue; + + if (page_jump(map,chip->base + chip->start,0x555,&base,0) != 0) + return -EIO; + + // Send the erase setup code + writeb(0xF0,base + 0x555); + writeb(0xAA,base + 0x555); + writeb(0x55,base + 0x2AA); + writeb(0x80,base + 0x555); + writeb(0xAA,base + 0x555); + writeb(0x55,base + 0x2AA); + + // Use chip erase if possible + if (chip->start == 0 && chip->length == chip->size) + { + writeb(0x10,base+0x555); + continue; + } + + /* Once we start selecting the erase sectors the delay between each + command must not exceed 50us or it will immediately start erasing + and ignore the other sectors */ + save_flags(flags); + cli(); + for (off = 0; off < chip->length; off += chip->sectorsize) + { + if (page_jump(map,chip->base + chip->start + off,1,&base,0) != 0) + return -EIO; + + // Check to make sure we didn't timeout + writeb(0x30,base); + if ((readb(base) & (1 << 3)) != 0) + { + printk("mtd: Ack! We timed out the erase timer!\n"); + return -EIO; + } + } + restore_flags(flags); + } + + /* We could split this into a timer routine and return early, performing + background erasure.. Maybe later if the need warrents */ + + /* Poll the flash for erasure completion, specs say this can take as long + as 480 seconds to do all the sectors (for a 2 meg flash). + Erasure time is dependant on chip age, temp and wear.. */ + Time = 0; + NoTime = 0; + for (I = 0; map->chips[I].jedec != 0; I++) + { + struct flash_chip *chip = map->chips + I; + unsigned long base; + unsigned long off = 0; + if (chip->length == 0) + continue; + + if (page_jump(map,chip->base + chip->start,1,&base,0) != 0) + return -EIO; + + while (1) + { + unsigned char Last[4]; + unsigned long Count = 0; + + /* During erase bit 7 is held low and bit 6 toggles, we watch this, + should it stop toggling or go high then the erase is completed, + or this is not really flash ;> */ + Last[0] = readb(base); + Last[1] = readb(base); + Last[2] = readb(base); + for (Count = 3; (Last[(Count - 1) % 4] & (1 << 7)) == 0 && + Last[(Count - 1) % 4] != Last[(Count - 2) % 4]; Count++) + { + if (NoTime == 0) + Time += HZ/10 - schedule_timeout(HZ/10); + NoTime = 0; + + Last[Count % 4] = readb(base); + + // Count time, max of 15s per sector (according to AMD) + if (Time > 15*len/mtd->erasesize*HZ) + { + printk("mtd: Flash Erase Timed out\n"); + return -EIO; + } + } + + if (Last[(Count - 1) % 4] == Last[(Count - 2) % 4]) + { + flash_failed(Last[(Count - 3) % 4]); + return -EIO; + } + + // Skip to the next chip if we used chip erase + if (chip->length == chip->size) + off = chip->size; + else + off += chip->sectorsize; + + if (off >= chip->length) + break; + if (page_jump(map,chip->base + chip->start + off,1,&base,0) != 0) + return -EIO; + NoTime = 1; + } + } + + // Paranoid verify of erasure + { + unsigned long base; + unsigned long buflen; + while (len > 0) + { + unsigned long step; + + if (page_jump(map,start,len,&base,&buflen) != 0) + return -EIO; + start += buflen; + len -= buflen; + step = buflen/128; + for (;buflen != 0; buflen -= step) + { + if (readb(base+buflen-1) != 0xFF) + { + printk("mtd: Flash Erase didn't take %lu %lu %lu\n",buflen,len,start); + return -EIO; + } + } + } + } + + return 0; +} +#if 1 + /*}}}*/ +// flash_write - Generic writing function /*{{{*/ +// --------------------------------------------------------------------- +/* This could do parallel writes on multiple chips but doesnt, memory + constraints make that infeasable. This should work with any sort of + linear flash that is not interleved */ +extern int flash_write(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mapped_mtd_info *map = (struct mapped_mtd_info *)mtd; + unsigned long base; + unsigned long off; + DEBUG(1,"\n"); + if (start + len > mtd->size) + return -EIO; + + while (len != 0) + { + // Compute the page offset and reposition + base = map->page(map,(u_long)start/map->pagesize); + off = (u_long)start % map->pagesize; + + // Loop over this page + for (; off != map->pagesize && len != 0; start++, len--, off++,buf++) + { + unsigned char oldbyte = readb(base+off); + unsigned char Last[4]; + unsigned long Count = 0; + + if (oldbyte == *buf) + continue; + if (((~oldbyte) & *buf) != 0) + printk("mtd: warn: Trying to set a 0 to a 1\n"); + + // Write + writeb(0xAA,base + 0x555); + writeb(0x55,base + 0x2AA); + writeb(0xA0,base + 0x555); + writeb(*buf,base + off); + Last[0] = readb(base + off); + Last[1] = readb(base + off); + Last[2] = readb(base + off); + + /* Wait for the flash to finish the operation. We store the last 4 + status bytes that have been retrieved so we can determine why + it failed. The toggle bits keep toggling when there is a + failure */ + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && + Count < 10000; Count++) + Last[Count % 4] = readb(base + off); + if (Last[(Count - 1) % 4] != *buf) + { + flash_failed(Last[(Count - 3) % 4]); + return -EIO; + } + } + } + *retlen = len; + return 0; +} +#endif + +// ram_write - Generic writing function for ram /*{{{*/ +// --------------------------------------------------------------------- +/* */ +extern int ram_write(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mapped_mtd_info *map = (struct mapped_mtd_info *)mtd; + unsigned long base; + size_t origlen = len; + unsigned long buflen; + DEBUG(1,"\n"); + if (start + len > mtd->size) + return -EIO; + + while (len != 0) + { + // Reposition.. + if (page_jump(map,start,len,&base,&buflen) != 0) + return -EIO; + + // Copy + memcpy_toio(base,buf,buflen); + len -= buflen; + start += buflen; + } + *retlen = origlen; + return 0; +} + +// rom_read - Read handler for any sort of device /*{{{*/ +// --------------------------------------------------------------------- +/* This is a generic read function that should work with any device in the + mapped region. */ +extern int rom_read(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u_char *buf) +{ + struct mapped_mtd_info *map = (struct mapped_mtd_info *)mtd; + size_t origlen = len; + unsigned long base; + unsigned long buflen; + + printk("Rom_Read\n"); + if (start + len > mtd->size) + return -EIO; + + while (len != 0) + { + // Reposition.. + if (page_jump(map,start,len,&base,&buflen) != 0) + return -EIO; + + // Copy + memcpy_fromio(buf,base,buflen); + len -= buflen; + start += buflen; + } + *retlen = origlen; + return 0; +} + +// page_jump - Move the window and return the buffer /*{{{*/ +// --------------------------------------------------------------------- +/* Unlike the page function this returns a buffer and length adjusted for + the page dimensions and the reading offset into the page, simplifies + many of the other routines */ +int page_jump(struct mapped_mtd_info *map,unsigned long start, + unsigned long len,unsigned long *base, + unsigned long *retlen) +{ + DEBUG(1,"Page Jump\n"); + if (start > map->mtd.size || start + len > map->mtd.size) + return -EINVAL; + + *base = map->page(map,start/map->pagesize); + if (*base == 0) + return -EIO; + + *base += start % map->pagesize; + + // If retlen is 0 that mean the caller requires len bytes, no quibbling. + if (retlen == 0) + { + if (len > map->pagesize - (start % map->pagesize)) + return -EIO; + return 0; + } + + // Compute the buffer paramaters and return + if (len > map->pagesize - (start % map->pagesize)) + *retlen = map->pagesize - (start % map->pagesize); + else + *retlen = len; + return 0; +} + /*}}}*/ +// flash_chip_scan - Intersect a region with the flash chip structure /*{{{*/ +// --------------------------------------------------------------------- +/* This is used to enhance the speed of the erase routine, + when things are being done to multiple chips it is possible to + parallize the operations, particularly full memory erases of multi + chip memories benifit */ + +void flash_chip_scan(struct mapped_mtd_info *map,unsigned long start, + unsigned long len) +{ + unsigned int I = 0; + + DEBUG(1,"\n"); + // Zero the records + for (I = 0; map->chips[I].jedec != 0; I++) + map->chips[I].start = map->chips[I].length = 0; + + // Intesect our region with the chip structures + for (I = 0; map->chips[I].jedec != 0 && len != 0; I++) + { + // Havent found the start yet + if (start >= map->chips[I].base + map->chips[I].size) + continue; + + // Store the portion of this chip that is being effected + map->chips[I].start = start - map->chips[I].base; + if (len <= map->chips[I].size - map->chips[I].start) + map->chips[I].length = len; + else + map->chips[I].length = map->chips[I].size - map->chips[I].start; + len -= map->chips[I].length; + start = map->chips[I].base + map->chips[I].size; + } +} + /*}}}*/ + diff --git a/drivers/mtd/mixmem.c b/drivers/mtd/mixmem.c new file mode 100644 index 000000000..8326f864b --- /dev/null +++ b/drivers/mtd/mixmem.c @@ -0,0 +1,151 @@ +/* + * mixmem - a block device driver for flash rom found on the + * piggyback board of the multi-purpose mixcom card + * + * Author: Gergely Madarasz <gorgo@itc.hu> + * + * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu> + * + * This code is GPL + * + */ + +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <linux/init.h> +#include <linux/mtd/mapped.h> + +#define MIXCOM_ID_OFFSET 0xc10 +#define MIXCOM_PAGE_OFFSET 0xc11 +#define MIXCOM_ID_1 0x11 +#define MIXCOM_ID_2 0x13 +#define MIXMEM_PAGESIZE 4096 +#define FIRST_BLOCK_OFFSET 0x1000 + +#if LINUX_VERSION_CODE < 0x20300 +#define __exit +#endif + +static unsigned int mixmem_addrs[] = { 0xc8000, 0xd8000, 0 }; +static unsigned int mixcom_ports[] = { 0x180, 0x280, 0x380, 0 }; + +// We could store these in the mtd structure, but we only support 1 device.. +static unsigned long base_io = 0; +static unsigned long base_addr = 0; +static struct mapped_mtd_info *SSD; + +static unsigned long mixmem_page(struct mapped_mtd_info *map, + unsigned long page) +{ + outb((char)(page & 0xff), base_io+MIXCOM_PAGE_OFFSET); + outb((char)((page >> 8) & 0x7), base_io+MIXCOM_PAGE_OFFSET+1); + return base_addr; +} + +static int flash_probe(int base) +{ + int prev,curr; + unsigned long flags; + + writeb(0xf0, base); + save_flags(flags); cli(); + + prev=readw(base); + + writeb(0xaa, base+0x555); + writeb(0x55, base+0x2AA); + writeb(0x90, base+0x555); + + curr=readw(base); + + restore_flags(flags); + writeb(0xf0, base); + return(prev==curr?0:curr); +} + +static int mixmem_probe(void) +{ + int i; + int id; + int chip; + + /* This should really check to see if the io ports are in use before + writing to them */ + for(i=0;mixcom_ports[i]!=0;i++) { + id=inb(mixcom_ports[i]+MIXCOM_ID_OFFSET); + if(id==MIXCOM_ID_1 || id==MIXCOM_ID_2) { + printk("mixmem: mixcom board found at 0x%3x\n",mixcom_ports[i]); + break; + } + } + + if(mixcom_ports[i]==0) { + printk("mixmem: no mixcom board found\n"); + return -ENODEV; + } + + if (check_region(mixcom_ports[i]+MIXCOM_PAGE_OFFSET, 2)) return -EAGAIN; + + + + // What is the deal with first_block_offset? + for(i=0;mixmem_addrs[i]!=0;i++) { + chip=flash_probe(mixmem_addrs[i]+FIRST_BLOCK_OFFSET); + if(chip)break; + } + + if(mixmem_addrs[i]==0) { + printk("mixmem: no flash available\n"); + return -ENODEV; + } + base_io = mixcom_ports[i]; + base_addr = mixmem_addrs[i]; + request_region(mixcom_ports[i]+MIXCOM_PAGE_OFFSET, 2, "mixmem"); + return 0; +} + + +static void __exit cleanup_mixmem() +{ + mtd_mapped_remove(SSD); + kfree(SSD); + SSD = 0; + release_region(base_io+MIXCOM_PAGE_OFFSET, 2); +} + +//static int __init init_mixmem(void) +int __init init_mixmem(void) +{ + if (mixmem_probe() != 0) + return -EAGAIN; + + // Print out our little header.. + printk("mixcom MTD IO:0x%lx MEM:0x%lx-0x%lx\n",base_io,base_addr, + base_addr+MIXMEM_PAGESIZE); + + // Allocate some memory + SSD = (struct mapped_mtd_info *)kmalloc(sizeof(*SSD),GFP_KERNEL); + if (SSD == 0) + return 0; + memset(SSD,0,sizeof(*SSD)); + + // Setup the MTD structure + SSD->page = mixmem_page; + SSD->pagesize = MIXMEM_PAGESIZE; + SSD->maxsize = 0x7FF; + SSD->mtd.name = "mixcom piggyback"; + + // Setup the MTD, this will sense the flash parameters and so on.. + if (mtd_mapped_setup(SSD) != 0) + { + printk("Failed to register new device\n"); + cleanup_module(); + return -EAGAIN; + } + + return 0; +} +module_init(init_mixmem); +module_exit(cleanup_mixmem); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c new file mode 100644 index 000000000..2d7b5d5fe --- /dev/null +++ b/drivers/mtd/mtdblock.c @@ -0,0 +1,318 @@ +/* + * Direct MTD block device access + * + * $Id: mtdblock.c,v 1.16 2000/06/23 09:34:58 dwmw2 Exp $ + */ + +#ifdef MTDBLOCK_DEBUG +#define DEBUGLVL debug +#endif + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/config.h> + +#include <linux/mtd/mtd.h> + +#define MAJOR_NR MTD_BLOCK_MAJOR +#define DEVICE_NAME "mtdblock" +#define DEVICE_REQUEST mtdblock_request +#define DEVICE_NR(device) (device) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) +#define DEVICE_NO_RANDOM +#include <linux/blk.h> + +#if LINUX_VERSION_CODE < 0x20300 +#define RQFUNC_ARG void +#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +#else +#define RQFUNC_ARG request_queue_t *q +#endif + +#ifdef MTDBLOCK_DEBUG +static int debug = MTDBLOCK_DEBUG; +MODULE_PARM(debug, "i"); +#endif + +#if 1 +static void mtdblock_end_request(struct request *req, int res) +{ + if (end_that_request_first( req, res, "mtdblock" )) + return; + end_that_request_last( req ); +} +#endif + +static int mtd_sizes[MAX_MTD_DEVICES]; + + +/* Keeping a separate list rather than just getting stuff directly out of + the MTD core's mtd_table is perhaps not very nice, but I happen + to dislike the idea of directly accessing mtd_table even more. + dwmw2 31/3/0 +*/ + +static int mtdblock_open(struct inode *inode, struct file *file) +{ + struct mtd_info *mtd = NULL; + + int dev; + + DEBUG(1,"mtdblock_open\n"); + + if (inode == 0) + return -EINVAL; + + dev = MINOR(inode->i_rdev); + + MOD_INC_USE_COUNT; + + mtd = get_mtd_device(NULL, dev); + + if (!mtd) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + mtd_sizes[dev] = mtd->size>>9; + + DEBUG(1, "ok\n"); + + return 0; +} + +static release_t mtdblock_release(struct inode *inode, struct file *file) +{ + int dev; + struct mtd_info *mtd; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + struct super_block * sb = get_super(inode->i_rdev); +#endif + DEBUG(1, "mtdblock_release\n"); + + if (inode == NULL) + release_return(-ENODEV); + + fsync_dev(inode->i_rdev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (sb) invalidate_inodes(sb); +#endif + invalidate_buffers(inode->i_rdev); + + dev = MINOR(inode->i_rdev); + mtd = __get_mtd_device(NULL, dev); + + if (!mtd) { + printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); + MOD_DEC_USE_COUNT; + release_return(-ENODEV); + + } + + if (mtd->sync) + mtd->sync(mtd); + + put_mtd_device(mtd); + + DEBUG(1, "ok\n"); + + MOD_DEC_USE_COUNT; + release_return(0); +} + + +static void mtdblock_request(RQFUNC_ARG) +{ + struct request *current_request; + unsigned int res = 0; + struct mtd_info *mtd; + + while (1) + { + /* Grab the Request and unlink it from the request list, INIT_REQUEST + will execute a return if we are done. */ + INIT_REQUEST; + current_request = CURRENT; + + if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) + { + printk("mtd: Unsupported device!\n"); + end_request(0); + continue; + } + + // Grab our MTD structure + + mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); + if (!mtd) { + printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); + end_request(0); + } + + if (current_request->sector << 9 > mtd->size || + (current_request->sector + current_request->nr_sectors) << 9 > mtd->size) + { + printk("mtd: Attempt to read past end of device!\n"); + printk("size: %lx, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors); + end_request(0); + continue; + } + + /* Remove the request we are handling from the request list so nobody messes + with it */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + blkdev_dequeue_request(current_request); + + /* Now drop the lock that the ll_rw_blk functions grabbed for us + and process the request. This is necessary due to the extreme time + we spend processing it. */ + spin_unlock_irq(&io_request_lock); +#endif + + // Handle the request + switch (current_request->cmd) + { + size_t retlen; + + case READ: + if (mtd->read(mtd,current_request->sector<<9, + current_request->nr_sectors << 9, + &retlen, current_request->buffer) == 0) + res = 1; + else + res = 0; + break; + + case WRITE: +//printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, +// current_request->nr_sectors); + + // Read only device + if ((mtd->flags & MTD_CAP_RAM) == 0) + { + res = 0; + break; + } + + // Do the write + if (mtd->write(mtd,current_request->sector<<9, + current_request->nr_sectors << 9, + &retlen, current_request->buffer) == 0) + res = 1; + else + res = 0; + break; + + // Shouldn't happen + default: + printk("mtd: unknown request\n"); + break; + } + + // Grab the lock and re-thread the item onto the linked list +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + spin_lock_irq(&io_request_lock); + mtdblock_end_request(current_request, res); +#else + end_request(res); +#endif + } +} + + + +static int mtdblock_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct mtd_info *mtd; + + mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); + + if (!mtd) return -EINVAL; + + switch (cmd) { + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EFAULT; + return put_user((mtd->size >> 9), + (long *) arg); + + case BLKFLSBUF: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if(!capable(CAP_SYS_ADMIN)) return -EACCES; +#endif + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + if (mtd->sync) + mtd->sync(mtd); + return 0; + + default: + return -EINVAL; + } +} + + /*}}}*/ +#if LINUX_VERSION_CODE < 0x20326 +static struct file_operations mtd_fops = +{ + open: mtdblock_open, + ioctl: mtdblock_ioctl, + release: mtdblock_release, + read: block_read, + write: block_write +}; +#else +static struct block_device_operations mtd_fops = +{ + open: mtdblock_open, + release: mtdblock_release, + ioctl: mtdblock_ioctl +}; +#endif + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_mtdblock init_module +#define cleanup_mtdblock cleanup_module +#endif +#define __exit +#endif + + +int __init init_mtdblock(void) +{ + int i; + + if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_BLOCK_MAJOR); + return EAGAIN; + } + + /* We fill it in at open() time. */ + for (i=0; i< MAX_MTD_DEVICES; i++) { + mtd_sizes[i] = 0; + } + + /* Allow the block size to default to BLOCK_SIZE. */ + blksize_size[MAJOR_NR] = NULL; + blk_size[MAJOR_NR] = mtd_sizes; + +#if LINUX_VERSION_CODE < 0x20320 + blk_dev[MAJOR_NR].request_fn = mtdblock_request; +#else + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +#endif + return 0; +} + +static void __exit cleanup_mtdblock(void) +{ + unregister_blkdev(MAJOR_NR,DEVICE_NAME); +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_mtdblock); +module_exit(cleanup_mtdblock); +#endif diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c new file mode 100644 index 000000000..d0e79a431 --- /dev/null +++ b/drivers/mtd/mtdchar.c @@ -0,0 +1,402 @@ +/* + * $Id: mtdchar.c,v 1.7 2000/06/30 15:54:19 dwmw2 Exp $ + * + * Character-device access to raw MTD devices. + * + */ + + +#include <linux/mtd/compatmac.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/malloc.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) +#else +static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int orig) +#endif +{ + struct mtd_info *mtd=(struct mtd_info *)file->private_data; + + switch (orig) { + case 0: + /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: + /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: + /* SEEK_END */ + file->f_pos =mtd->size + offset; + break; + default: + return -EINVAL; + } + + if (file->f_pos < 0) + file->f_pos = 0; + else if (file->f_pos >= mtd->size) + file->f_pos = mtd->size - 1; + + return file->f_pos; +} + + + +static int mtd_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + int devnum = minor >> 1; + struct mtd_info *mtd; + + DEBUG(0, "MTD_open\n"); + + if (devnum >= MAX_MTD_DEVICES) + return -ENODEV; + + /* You can't open the RO devices RW */ + if ((file->f_mode & 2) && (minor & 1)) + return -EACCES; + + MOD_INC_USE_COUNT; + + mtd = get_mtd_device(NULL, devnum); + + if (!mtd) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + file->private_data = mtd; + + /* You can't open it RW if it's not a writeable device */ + if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { + put_mtd_device(mtd); + MOD_DEC_USE_COUNT; + return -EACCES; + } + + return 0; +} /* mtd_open */ + +/*====================================================================*/ + +static release_t mtd_close(struct inode *inode, + struct file *file) +{ + struct mtd_info *mtd; + + DEBUG(0, "MTD_close\n"); + + mtd = (struct mtd_info *)file->private_data; + + if (mtd->sync) + mtd->sync(mtd); + + put_mtd_device(mtd); + + MOD_DEC_USE_COUNT; + release_return(0); +} /* mtd_close */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +#define FILE_POS *ppos +#else +#define FILE_POS file->f_pos +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) +#else +static int mtd_read(struct inode *inode,struct file *file, char *buf, int count) +#endif +{ + struct mtd_info *mtd = (struct mtd_info *)file->private_data; + size_t retlen=0; + int ret=0; + char *kbuf; + + DEBUG(0,"MTD_read\n"); + + if (FILE_POS + count > mtd->size) + count = mtd->size - FILE_POS; + + if (!count) + return 0; + + /* FIXME: Use kiovec in 2.3 or 2.2+rawio, or at + * least split the IO into smaller chunks. + */ + + kbuf = vmalloc(count); + if (!kbuf) + return -ENOMEM; + + ret = MTD_READ(mtd, FILE_POS, count, &retlen, kbuf); + if (!ret) { + FILE_POS += retlen; + if (copy_to_user(buf, kbuf, retlen)) + ret = -EFAULT; + else + ret = retlen; + + } + + vfree(kbuf); + + return ret; +} /* mtd_read */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos) +#else +static read_write_t mtd_write(struct inode *inode,struct file *file, const char *buf, count_t count) +#endif +{ + struct mtd_info *mtd = (struct mtd_info *)file->private_data; + char *kbuf; + size_t retlen; + int ret=0; + + DEBUG(0,"MTD_write\n"); + + if (FILE_POS == mtd->size) + return -ENOSPC; + + if (FILE_POS + count > mtd->size) + count = mtd->size - FILE_POS; + + if (!count) + return 0; + + kbuf=vmalloc(count); + + if (!kbuf) + return -ENOMEM; + + if (copy_from_user(kbuf, buf, count)) { + vfree(kbuf); + return -EFAULT; + } + + + ret = (*(mtd->write))(mtd, FILE_POS, count, &retlen, buf); + + if (!ret) { + FILE_POS += retlen; + ret = retlen; + } + + vfree(kbuf); + + return ret; +} /* mtd_write */ + +/*====================================================================== + + IOCTL calls for getting device parameters. + +======================================================================*/ +static void mtd_erase_callback (struct erase_info *instr) +{ + wake_up((wait_queue_head_t *)instr->priv); +} + +static int mtd_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) +{ + struct mtd_info *mtd = (struct mtd_info *)file->private_data; + int ret = 0; + u_long size; + + DEBUG(0, "MTD_ioctl\n"); + + size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; + if (cmd & IOC_IN) { + ret = verify_area(VERIFY_READ, (char *)arg, size); + if (ret) return ret; + } + if (cmd & IOC_OUT) { + ret = verify_area(VERIFY_WRITE, (char *)arg, size); + if (ret) return ret; + } + + switch (cmd) { + case MEMGETINFO: + copy_to_user((struct mtd_info *)arg, mtd, + sizeof(struct mtd_info_user)); + break; + + case MEMERASE: + { + struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); + if (!erase) + ret = -ENOMEM; + else { + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + init_waitqueue_head(&waitq); + + memset (erase,0,sizeof(struct erase_info)); + copy_from_user(&erase->addr, (u_long *)arg, + 2 * sizeof(u_long)); + erase->mtd = mtd; + erase->callback = mtd_erase_callback; + erase->priv = (unsigned long)&waitq; + + /* FIXME: Allow INTERRUPTIBLE. Which means + not having the wait_queue head on the stack + + Does it? Why? Who wrote this? Was it my alter + ago - the intelligent one? Or was it the stupid + one, and now I'm being clever I don't know what + it was on about? + + dwmw2. + + It was the intelligent one. If the wq_head is + on the stack, and we leave because we got + interrupted, then the wq_head is no longer + there when the callback routine tries to + wake us up --> BOOM!. + + */ + current->state = TASK_UNINTERRUPTIBLE; + add_wait_queue(&waitq, &wait); + ret = mtd->erase(mtd, erase); + if (!ret) + schedule(); + remove_wait_queue(&waitq, &wait); + current->state = TASK_RUNNING; + if (!ret) + ret = (erase->state == MTD_ERASE_FAILED); + kfree(erase); + } + break; + } + + case MEMWRITEOOB: + { + struct mtd_oob_buf buf; + void *databuf; + ssize_t retlen; + + copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + + if (buf.length > 0x4096) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); + + if (ret) + return ret; + + databuf = kmalloc(buf.length, GFP_KERNEL); + if (!databuf) + return -ENOMEM; + + copy_from_user(databuf, buf.ptr, buf.length); + + ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); + + copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); + + kfree(databuf); + break; + + } + + case MEMREADOOB: + { + struct mtd_oob_buf buf; + void *databuf; + ssize_t retlen; + + copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)); + + if (buf.length > 0x4096) + return -EINVAL; + + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else + ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); + + if (ret) + return ret; + + databuf = kmalloc(buf.length, GFP_KERNEL); + if (!databuf) + return -ENOMEM; + + ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); + + copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)); + + if (retlen) + copy_to_user(buf.ptr, databuf, retlen); + + kfree(databuf); + break; + } + + + + + + default: + printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO); + ret = -EINVAL; + } + + return ret; +} /* memory_ioctl */ + +static struct file_operations mtd_fops = { + + llseek: mtd_lseek, /* lseek */ + read: mtd_read, /* read */ + write: mtd_write, /* write */ + ioctl: mtd_ioctl, /* ioctl */ + open: mtd_open, /* open */ + release: mtd_close, /* release */ +}; + + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_mtdchar init_module +#define cleanup_mtdchar cleanup_module +#endif +#endif + +mod_init_t init_mtdchar(void) +{ + + if (register_chrdev(MTD_CHAR_MAJOR,"mtd",&mtd_fops)) { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return EAGAIN; + } + + return 0; +} + +mod_exit_t cleanup_mtdchar(void) +{ + unregister_chrdev(MTD_CHAR_MAJOR,"mtd"); +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_mtdchar); +module_exit(cleanup_mtdchar); +#endif diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c new file mode 100644 index 000000000..bbfc66eaa --- /dev/null +++ b/drivers/mtd/mtdcore.c @@ -0,0 +1,398 @@ +/* + * $Id: mtdcore.c,v 1.8 2000/06/27 13:40:05 dwmw2 Exp $ + * + * Core registration and callback routines for MTD + * drivers and users. + * + */ + +#ifdef MTD_DEBUG +#define DEBUGLVL debug +#endif + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <stdarg.h> +#include <linux/mtd/compatmac.h> +#ifdef CONFIG_PROC_FS +#include <linux/proc_fs.h> +#endif + +#include <linux/mtd/mtd.h> + +#ifdef MTD_DEBUG +static int debug = MTD_DEBUG; +MODULE_PARM(debug, "i"); +#endif + +/* Init code required for 2.2 kernels */ + +#if LINUX_VERSION_CODE < 0x20300 + +#ifdef CONFIG_MTD_DOC1000 +extern int init_doc1000(void); +#endif +#ifdef CONFIG_MTD_DOCPROBE +extern int init_doc(void); +#endif +#ifdef CONFIG_MTD_OCTAGON +extern int init_octagon5066(void); +#endif +#ifdef CONFIG_MTD_VMAX +extern int init_vmax301(void); +#endif +#ifdef CONFIG_MTD_MIXMEM +extern int init_mixmem(void); +#endif +#ifdef CONFIG_MTD_PMC551 +extern int init_pmc551(void); +#endif +#ifdef CONFIG_MTD_NORA +extern int init_nora(void); +#endif +#ifdef CONFIG_FTL +extern int init_ftl(void); +#endif +#ifdef CONFIG_NFTL +extern int init_nftl(void); +#endif +#ifdef CONFIG_MTD_BLOCK +extern int init_mtdblock(void); +#endif +#ifdef CONFIG_MTD_CHAR +extern int init_mtdchar(void); +#endif + +#endif /* LINUX_VERSION_CODE < 0x20300 */ + + +static DECLARE_MUTEX(mtd_table_mutex); + +static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; + +static struct mtd_notifier *mtd_notifiers = NULL; + + +int add_mtd_device(struct mtd_info *mtd) +{ + int i; + + down(&mtd_table_mutex); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (!mtd_table[i]) + { + struct mtd_notifier *not=mtd_notifiers; + + mtd_table[i] = mtd; + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); + while (not) + { + (*(not->add))(mtd); + not = not->next; + } + up(&mtd_table_mutex); + MOD_INC_USE_COUNT; + return 0; + } + + up(&mtd_table_mutex); + return 1; +} + + +int del_mtd_device (struct mtd_info *mtd) +{ + struct mtd_notifier *not=mtd_notifiers; + int i; + + down(&mtd_table_mutex); + + for (i=0; i < MAX_MTD_DEVICES; i++) + { + if (mtd_table[i] == mtd) + { + while (not) + { + (*(not->remove))(mtd); + not = not->next; + } + mtd_table[i] = NULL; + up (&mtd_table_mutex); + MOD_DEC_USE_COUNT; + return 0; + } + } + + up(&mtd_table_mutex); + return 1; +} + + + +void register_mtd_user (struct mtd_notifier *new) +{ + int i; + + down(&mtd_table_mutex); + + new->next = mtd_notifiers; + mtd_notifiers = new; + + MOD_INC_USE_COUNT; + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + new->add(mtd_table[i]); + + up(&mtd_table_mutex); +} + + + +int unregister_mtd_user (struct mtd_notifier *old) +{ + struct mtd_notifier **prev = &mtd_notifiers; + struct mtd_notifier *cur; + int i; + + down(&mtd_table_mutex); + + while ((cur = *prev)) { + if (cur == old) { + *prev = cur->next; + + MOD_DEC_USE_COUNT; + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + old->remove(mtd_table[i]); + + up(&mtd_table_mutex); + return 0; + } + prev = &cur->next; + } + up(&mtd_table_mutex); + return 1; +} + + +/* get_mtd_device(): + * Prepare to use an MTD device referenced either by number or address. + * + * If <num> == -1, search the table for an MTD device located at <mtd>. + * If <mtd> == NULL, return the MTD device with number <num>. + * If both are set, return the MTD device with number <num> _only_ if it + * is located at <mtd>. + */ + +struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) +{ + struct mtd_info *ret = NULL; + int i; + + down(&mtd_table_mutex); + + if (num == -1) { + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i] == mtd) + ret = mtd_table[i]; + } else if (num < MAX_MTD_DEVICES) { + ret = mtd_table[num]; + if (mtd && mtd != ret) + ret = NULL; + } + + up(&mtd_table_mutex); + return ret; +} + +EXPORT_SYMBOL(add_mtd_device); +EXPORT_SYMBOL(del_mtd_device); +EXPORT_SYMBOL(__get_mtd_device); +EXPORT_SYMBOL(register_mtd_user); +EXPORT_SYMBOL(unregister_mtd_user); + +/*====================================================================*/ +/* /proc/mtd support */ + +#ifdef CONFIG_PROC_FS + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +struct proc_dir_entry *proc_mtd; +#endif + +static inline int mtd_proc_info (char *buf, int i) +{ + struct mtd_info *this = mtd_table[i]; + + if (!this) + return 0; + + return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size, + this->name); +} + +static int mtd_read_proc ( char *page, char **start, off_t off,int count +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +#else + ,int unused +#endif + ) +{ + int len = 0, l, i; + off_t begin = 0; + + down(&mtd_table_mutex); + + for (i=0; i< MAX_MTD_DEVICES; i++) { + + l = mtd_proc_info(page + len, i); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + *eof = 1; +#endif + +done: + up(&mtd_table_mutex); + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +struct proc_dir_entry mtd_proc_entry = { + 0, /* low_ino: the inode -- dynamic */ + 3, "mtd", /* len of name and name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, /* nlinks, owner, group */ + 0, NULL, /* size - unused; operations -- use default */ + &mtd_read_proc, /* function used to read data */ + /* nothing more */ + }; +#endif + +#endif + +/*====================================================================*/ + + +#if LINUX_VERSION_CODE < 0x20300 + +static inline void init_others(void) +{ + /* Shedloads of calls to init functions of all the + * other drivers and users of MTD, which we can + * ditch in 2.3 because of the sexy new way of + * finding init routines. + */ +#ifdef CONFIG_MTD_DOC1000 + init_doc1000(); +#endif +#ifdef CONFIG_MTD_DOCPROBE + init_doc(); /* This covers both the DiskOnChip 2000 + * and the DiskOnChip Millennium. + * Theoretically all other DiskOnChip + * devices too. */ +#endif +#ifdef CONFIG_MTD_OCTAGON + init_octagon5066(); +#endif +#ifdef CONFIG_MTD_VMAX + init_vmax301(); +#endif +#ifdef CONFIGF_MTD_MIXMEM + init_mixmem(); +#endif +#ifdef CONFIG_MTD_PMC551 + init_pmc551(); +#endif +#ifdef CONFIG_MTD_NORA + init_nora(); +#endif +#ifdef CONFIG_MTD_MTDRAM + init_mtdram(); +#endif +#ifdef CONFIG_FTL + init_ftl(); +#endif +#ifdef CONFIG_NFTL + init_nftl(); +#endif +#ifdef CONFIG_MTD_BLOCK + init_mtdblock(); +#endif +#ifdef CONFIG_MTD_CHAR + init_mtdchar(); +#endif +} + +#ifdef MODULE +#define init_mtd init_module +#define cleanup_mtd cleanup_module +#endif + +#endif /* LINUX_VERSION_CODE < 0x20300 */ + +mod_init_t init_mtd(void) +{ + int i; + DEBUG(1, "INIT_MTD:\n"); + for (i=0; i<MAX_MTD_DEVICES; i++) + mtd_table[i]=NULL; + +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if ((proc_mtd = create_proc_entry( "mtd", 0, 0 ))) + proc_mtd->read_proc = mtd_read_proc; +#else + proc_register_dynamic(&proc_root,&mtd_proc_entry); +#endif + +#endif + +#if LINUX_VERSION_CODE < 0x20300 + init_others(); +#endif + + return 0; +} + +mod_exit_t cleanup_mtd(void) +{ + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if (proc_mtd) + remove_proc_entry( "mtd", 0); +#else + proc_unregister(&proc_root,mtd_proc_entry.low_ino); +#endif +#endif +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_mtd); +module_exit(cleanup_mtd); +#endif + + diff --git a/drivers/mtd/mtdram.c b/drivers/mtd/mtdram.c new file mode 100644 index 000000000..674435272 --- /dev/null +++ b/drivers/mtd/mtdram.c @@ -0,0 +1,147 @@ +/* + * mtdram - a test mtd device + * $Id: mtdram.c,v 1.13 2000/07/03 10:01:38 dwmw2 Exp $ + * Author: Alexander Larsson <alex@cendio.se> + * + * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> + * + * This code is GPL + * + */ + +#include <linux/module.h> + +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> + + +#define MTDRAM_TOTAL_SIZE 1024*1024*8 +#define MTDRAM_ERASE_SIZE 4*1024 + + +// We could store these in the mtd structure, but we only support 1 device.. +static struct mtd_info *mtd_info; + + +static int +ram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + memset((char *)mtd->priv + instr->addr, 0xff, instr->len); + + instr->state = MTD_ERASE_DONE; + + if (instr->callback) + (*(instr->callback))(instr); + return 0; +} + +static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) +{ + if (from + len > mtd->size) + return -EINVAL; + + *mtdbuf = mtd->priv + from; + *retlen = len; + return 0; +} + +static void ram_unpoint (struct mtd_info *mtd, u_char *addr) +{ +} + +static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + if (from + len > mtd->size) + return -EINVAL; + + memcpy(buf, mtd->priv + from, len); + + *retlen=len; + return 0; +} + +static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + if (to + len > mtd->size) + return -EINVAL; + + memcpy ((char *)mtd->priv + to, buf, len); + + *retlen=len; + return 0; +} + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_mtdram init_module +#define cleanup_mtdram cleanup_module +#endif +#endif + +//static void __exit cleanup_mtdram(void) +mod_exit_t cleanup_mtdram(void) +{ + if (mtd_info) { + del_mtd_device(mtd_info); + if (mtd_info->priv) + vfree(mtd_info->priv); + kfree(mtd_info); + } +} + +extern struct module __this_module; + +mod_init_t init_mtdram(void) +{ + // Allocate some memory + mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (mtd_info == 0) + return 0; + + memset(mtd_info, 0, sizeof(*mtd_info)); + + // Setup the MTD structure + mtd_info->name = "mtdram test device"; + mtd_info->type = MTD_RAM; + mtd_info->flags = MTD_CAP_RAM; + mtd_info->size = MTDRAM_TOTAL_SIZE; + mtd_info->erasesize = MTDRAM_ERASE_SIZE; + mtd_info->priv = vmalloc(MTDRAM_TOTAL_SIZE); + memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + mtd_info->module = THIS_MODULE; +#endif + + if (!mtd_info->priv) { + kfree(mtd_info); + mtd_info = NULL; + return -ENOMEM; + } + mtd_info->erase = ram_erase; + mtd_info->point = ram_point; + mtd_info->unpoint = ram_unpoint; + mtd_info->read = ram_read; + mtd_info->write = ram_write; + + if (add_mtd_device(mtd_info)) { + vfree(mtd_info->priv); + kfree(mtd_info); + mtd_info = NULL; + return -EIO; + } + + return 0; +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_mtdram); +module_exit(cleanup_mtdram); +#endif diff --git a/drivers/mtd/nftl.c b/drivers/mtd/nftl.c new file mode 100644 index 000000000..083b1219e --- /dev/null +++ b/drivers/mtd/nftl.c @@ -0,0 +1,1317 @@ + +/* Linux driver for NAND Flash Translation Layer */ +/* (c) 1999 Machine Vision Holdings, Inc. */ +/* Author: David Woodhouse <dwmw2@infradead.org */ +/* $Id: nftl.c,v 1.34 2000/06/07 14:48:52 dwmw2 Exp $ */ + +/* + LEGAL NOTE: The NFTL format is patented by M-Systems. They have + granted a licence for its use with their DiskOnChip products: + + "M-Systems grants a royalty-free, non-exclusive license under + any presently existing M-Systems intellectual property rights + necessary for the design and development of NFTL-compatible + drivers, file systems and utilities to use the data formats with, + and solely to support, M-Systems' DiskOnChip products" + + A signed copy of this agreement from M-Systems is kept on file by + Red Hat UK Limited. In the unlikely event that you need access to it, + please contact dwmw2@redhat.com for assistance. +*/ + +#define PRERELEASE + +#ifdef NFTL_DEBUG +#define DEBUGLVL debug +#endif + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nftl.h> +#include <linux/mtd/compatmac.h> + +#undef WE_KNOW_WTF_THIS_DOES_NOT_WORK + +/* NFTL block device stuff */ +#define MAJOR_NR NFTL_MAJOR +#define DEVICE_REQUEST nftl_request +#define DEVICE_OFF(device) +#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK +#define LOCAL_END_REQUEST +#endif +#include <linux/blk.h> +#include <linux/hdreg.h> + + +#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK + +static void nftl_end_request(struct request *req, int res) +{ + req->sector += req->current_nr_sectors; + req->nr_sectors -= req->current_nr_sectors; + + if (end_that_request_first( req, res, "nftl" )) + return; + end_that_request_last( req ); +} +#endif + +#ifdef NFTL_DEBUG +static int debug = NFTL_DEBUG; +MODULE_PARM(debug, "i"); +#endif + + + +/* Linux-specific block device functions */ + +/* I _HATE_ the Linux block device setup more than anything else I've ever + * encountered, except ... + */ + +static int nftl_sizes[256]={0,}; +static int nftl_blocksizes[256] = {0,}; + +/* .. for the Linux partition table handling. */ + +struct hd_struct part_table[256] = {{0,0},}; + +#if LINUX_VERSION_CODE < 0x20328 +static void dummy_init (struct gendisk *crap) +{} +#endif + +static struct gendisk nftl_gendisk = { + NFTL_MAJOR, /* Major number */ + "nftl", /* Major name */ + 4, /* Bits to shift to get real from partition */ + 15, /* Number of partitions per real */ +#if LINUX_VERSION_CODE < 0x20328 + MAX_NFTLS, /* maximum number of real */ + dummy_init, /* init function */ +#endif + part_table, /* hd struct */ + nftl_sizes, /* block sizes */ + 0, /* number */ + NULL, /* internal use, not presently used */ + NULL /* next */ +}; + + +struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL}; + +static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs, + struct NFTLMediaHeader *hdr) +{ + int i; + struct NFTLrecord *thisNFTL; + unsigned long temp; + int firstfree = -1; + + DEBUG(1,"NFTL_setup\n"); + + for (i=0; i < MAX_NFTLS; i++) { + if (!NFTLs[i] && firstfree==-1) + firstfree = i; + else if (NFTLs[i] && NFTLs[i]->mtd == mtd && + NFTLs[i]->MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN) { + /* This is a Spare Media Header for an NFTL we've already found */ + DEBUG(1, "Spare Media Header for NFTL %d found at %lx\n",i, ofs); + NFTLs[i]->SpareMediaUnit = ofs / mtd->erasesize; + return; + } + } + + + /* OK, it's a new one. Set up all the data structures. */ +#ifdef PSYCHO_DEBUG + printk("Found new NFTL nftl%c at offset %lx\n",firstfree + 'a', ofs); +#endif + if (hdr->UnitSizeFactor != 0xff) { + printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n"); + return; + } + + thisNFTL = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); + if (!thisNFTL) { + printk(KERN_WARNING "Out of memory for NFTL data structures\n"); + return; + } + init_MUTEX(&thisNFTL->mutex); + thisNFTL->EraseSize = mtd->erasesize; + memcpy(&thisNFTL->MediaHdr, hdr, sizeof(*hdr)); + thisNFTL->mtd = mtd; + thisNFTL->MediaUnit = ofs / mtd->erasesize; + thisNFTL->SpareMediaUnit = 0xffff; + thisNFTL->numvunits = le32_to_cpu(thisNFTL->MediaHdr.FormattedSize) / 8192; + thisNFTL->nr_sects = thisNFTL->numvunits * (thisNFTL->EraseSize / 512); + thisNFTL->usecount = 0; + + thisNFTL->cylinders = 1024; + thisNFTL->heads = 16; + + temp = thisNFTL->cylinders * thisNFTL->heads; + thisNFTL->sectors = thisNFTL->nr_sects / temp; + + if (thisNFTL->nr_sects % temp) { + + thisNFTL->sectors++; + temp = thisNFTL->cylinders * thisNFTL->sectors; + thisNFTL->heads = thisNFTL->nr_sects / temp; + + if (thisNFTL->nr_sects & temp) { + thisNFTL->heads++; + temp = thisNFTL->heads * thisNFTL->sectors; + + thisNFTL->cylinders = thisNFTL->nr_sects / temp; + } + } + if (thisNFTL->nr_sects != thisNFTL->heads * thisNFTL->cylinders * + thisNFTL->sectors) { + printk(KERN_WARNING "Cannot calculate an NFTL geometry to match size of 0x%lx.\n", thisNFTL->nr_sects); + printk(KERN_WARNING "Using C:%d H:%d S:%d (== %lx sects)\n", + thisNFTL->cylinders, thisNFTL->heads , + thisNFTL->sectors, + (long)thisNFTL->cylinders * (long)thisNFTL->heads * + (long)thisNFTL->sectors ); + + /* Oh no we don't + * thisNFTL->nr_sects = thisNFTL->heads * thisNFTL->cylinders * thisNFTL->sectors; + */ + } + + + thisNFTL->EUNtable = kmalloc( 2 * thisNFTL->numvunits, + GFP_KERNEL); + if (!thisNFTL->EUNtable) { + printk("ENOMEM\n"); + kfree(thisNFTL); + return; + } + memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits); + + thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); + if (!thisNFTL->VirtualUnitTable) { + printk("ENOMEM\n"); + kfree(thisNFTL->EUNtable); + kfree(thisNFTL); + return; + } + memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits)); + + thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL); + if (!thisNFTL->ReplUnitTable) { + printk("ENOMEM\n"); + kfree(thisNFTL->VirtualUnitTable); + kfree(thisNFTL->EUNtable); + kfree(thisNFTL); + return; + } + memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) ); + + /* Ought to check the media header for bad blocks */ + thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) + + le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1; + thisNFTL->numfreeEUNs = 0; + + /* Scan each physical Erase Unit for validity and to find the + Virtual Erase Unit Chain to which it belongs */ + + for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); + i <= thisNFTL->lastEUN; i++) { + + union nftl_uci uci; + unsigned long ofs; + size_t retlen; + ofs = i * thisNFTL->EraseSize; + + MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 512 + 8, 8, &retlen, (char *)&uci); + + if (uci.b.EraseMark != cpu_to_le16(0x3c69) || + uci.b.EraseMark1 != cpu_to_le16(0x3c69)) { + printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n", + i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1)); + thisNFTL->VirtualUnitTable[i] = 0x7fff; + thisNFTL->ReplUnitTable[i] = 0xffff; + continue; + } + + MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 8, 8, &retlen, (u_char *)&uci); + + if (uci.a.VirtUnitNum != uci.a.SpareVirtUnitNum) + printk("EUN %d: VirtualUnitNumber (%x) != SpareVirtualUnitNumber (%x)\n", + i, le16_to_cpu(uci.a.VirtUnitNum), + le16_to_cpu(uci.a.SpareVirtUnitNum)); + + if (uci.a.ReplUnitNum != uci.a.SpareReplUnitNum) + printk("EUN %d: ReplacementUnitNumber (%x) != SpareReplacementUnitNumber (%x)\n", + i, le16_to_cpu(uci.a.ReplUnitNum), + le16_to_cpu(uci.a.SpareReplUnitNum)); + + /* We don't actually _do_ anything about the above, just whinge */ + + thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum); + thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum); + + /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */ + if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits) + thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i; + + if (uci.a.VirtUnitNum == 0xffff) { + /* Free block */ + thisNFTL->LastFreeEUN = i; + thisNFTL->numfreeEUNs++; + } + + } + NFTLs[firstfree] = thisNFTL; + thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); + + //#define PSYCHO_DEBUG +#ifdef PSYCHO_DEBUG + for (i=0; i < 10/* thisNFTL->numvunits*/; i++) { + u16 curEUN = thisNFTL->EUNtable[i]; + int sillycount=100; + + printk("Virtual Unit #%d: ",i); + if (!curEUN || curEUN == 0xffff) { + printk("Not present\n"); + continue; + } + printk("%d", curEUN); + + while ((curEUN = thisNFTL->ReplUnitTable[curEUN]) != 0xffff && --sillycount) { + printk(", %d", curEUN & 0xffff); + + } + printk("\n"); + } +#endif + + /* OK. Now we deal with the fact that we're in the real world. Sometimes + things don't actually happen the way they're supposed to. Find, fix, + and whinge about the most common deviations from spec that we have + been known to encounter. + */ + /* Except that I haven't implemented that bit yet :) */ + + /* Finally, set up the block device sizes */ + nftl_sizes[firstfree * 16]=thisNFTL->nr_sects; +// nftl_blocksizes[firstfree*16] = 512; + part_table[firstfree * 16].nr_sects = thisNFTL->nr_sects; +#if LINUX_VERSION_CODE < 0x20328 + resetup_one_dev(&nftl_gendisk, firstfree); +#else + grok_partitions(&nftl_gendisk, firstfree, 1<<4, thisNFTL->nr_sects); +#endif + +} + + +static void NFTL_unsetup(int i) +{ + struct NFTLrecord *thisNFTL = NFTLs[i]; + + DEBUG(1, "NFTL_unsetup %d\n", i); + + NFTLs[i] = NULL; + + if (thisNFTL->VirtualUnitTable) + kfree(thisNFTL->VirtualUnitTable); + if (thisNFTL->ReplUnitTable) + kfree(thisNFTL->ReplUnitTable); + if (thisNFTL->EUNtable) + kfree(thisNFTL->EUNtable); + + kfree(thisNFTL); +} + + + + +/* Search the MTD device for NFTL partitions */ +static void NFTL_notify_add(struct mtd_info *mtd) +{ + int i; + unsigned long ofs; + struct NFTLMediaHeader hdr; + + DEBUG(1, "NFTL_notify_add for %s\n", mtd->name); + + if (mtd) { + if (!mtd->read_oob) /* If this MTD doesn't have out-of-band data, + then there's no point continuing */ + { + DEBUG(1, "No OOB data, quitting\n"); + return; + } + DEBUG(3, "mtd->read = %p,size = %d, erasesize = %d\n", + mtd->read, mtd->size, mtd->erasesize); + for (ofs = 0; ofs < mtd->size ; ofs += mtd->erasesize) { + size_t retlen = 0; + MTD_READ(mtd, ofs, sizeof(hdr), &retlen, (u_char *)&hdr); + + if (retlen < sizeof(hdr)) + { + continue; + } + + if (!strncmp(hdr.DataOrgID, "ANAND", 6)) { + DEBUG(2, "Valid NFTL partition at ofs %ld\n", ofs); + NFTL_setup(mtd, ofs, &hdr); + } + else { + DEBUG(3,"No valid NFTL Partition at ofs %d\n", ofs); + for(i = 0; i < 6; i++) { + DEBUG(3,"%x, ", hdr.DataOrgID[i]); + } + DEBUG(3," = %s\n", hdr.DataOrgID); + DEBUG(3,"%d, %d, %d, %d\n", hdr.NumEraseUnits, hdr.FirstPhysicalEUN, + hdr.FormattedSize, hdr.UnitSizeFactor); + + } + } + return; + } +} + +static void NFTL_notify_remove(struct mtd_info *mtd) +{ + int i; + + for (i=0; i< MAX_NFTLS; i++) { + if (NFTLs[i] && NFTLs[i]->mtd == mtd) + NFTL_unsetup(i); + } +} + + +#ifdef CONFIG_NFTL_RW + +/* Actual NFTL access routines */ + + +static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate ) +{ + /* For a given Virtual Unit Chain: find or create a free block and + add it to the chain */ + /* We're passed the number of the last EUN in the chain, to save us from + having to look it up again */ + + u16 pot = thisNFTL->LastFreeEUN; + int silly = -1; + + /* Normally, we force a fold to happen before we run out of free blocks completely */ + + if (!desperate && thisNFTL->numfreeEUNs < 2) { + // printk("NFTL_findfreeblock: there are too few free EUNs\n"); + return 0xffff; + } + + /* Scan for a free block */ + + do { + if (thisNFTL->VirtualUnitTable[pot] == 0xffff) { + thisNFTL->LastFreeEUN = pot; + thisNFTL->numfreeEUNs--; + return pot; + } + + if (++pot > thisNFTL->lastEUN) + pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN); + + if (!silly--) { + printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n", + thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN)); + return 0xffff; + } + + } while (pot != thisNFTL->LastFreeEUN); + + return 0xffff; +} + + + + + +static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock ) +{ + u16 BlockMap[thisNFTL->EraseSize / 512]; + unsigned char BlockLastState[thisNFTL->EraseSize / 512]; + unsigned char BlockFreeFound[thisNFTL->EraseSize / 512]; + u16 thisEUN; + int block; + int silly = -1; + u16 targetEUN = 0xffff; + struct nftl_oob oob; + int inplace = 1; + + memset(BlockMap, 0xff, sizeof(BlockMap)); + memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); + + thisEUN = thisNFTL->EUNtable[thisVUC]; + + if (thisEUN == 0xffff) { + printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC); + return 0xffff; + } + + /* Scan to find the Erase Unit which holds the actual data for each + 512-byte block within the Chain. + */ + + while( thisEUN <= thisNFTL->lastEUN ) { + size_t retlen; + + targetEUN = thisEUN; + + for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) { + + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob); + + if (block == 2) { + if (oob.u.c.WriteInh != 0xffffffff) { + printk("Write Inhibited on EUN %d\n", thisEUN); + inplace = 0; + } else { + /* There's no other reason not to do inplace, + except ones that come later. So we don't need + to preserve inplace */ + inplace = 1; + } + } + + BlockLastState[block] = (unsigned char) oob.b.Status & 0xff; + + switch(oob.b.Status) { + case __constant_cpu_to_le16(BLOCK_FREE): + BlockFreeFound[block]=1; + break; + + case __constant_cpu_to_le16(BLOCK_USED): + if (!BlockFreeFound[block]) + BlockMap[block] = thisEUN; + else + printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block); + break; + case __constant_cpu_to_le16(BLOCK_IGNORE): + case __constant_cpu_to_le16(BLOCK_DELETED): + break; + default: + printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status); + } + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; + } + + thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + } + + if (inplace) { + /* We're being asked to be a fold-in-place. Check + that all blocks are either present or BLOCK_FREE + in the target block. If not, we're going to have + to fold out-of-place anyway. + */ + + for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { + + if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) && + BlockMap[block] != targetEUN) { + DEBUG(1, "Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n", + thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN); + + inplace = 0; + break; + } + } + + if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) && + pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) && + BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] != + (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) { + DEBUG(1, "Pending write not free in EUN %d. Folding out of place.\n", targetEUN); + inplace = 0; + } + + } + + if (!inplace) { + DEBUG(1, "Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", thisVUC); + /* We need to find a targetEUN to fold into. */ + targetEUN = NFTL_findfreeblock(thisNFTL, 1); + if (targetEUN == 0xffff) { + /* Ouch. Now we're screwed. We need to do a + fold-in-place of another chain to make room + for this one. We need a better way of selecting + which chain to fold, because makefreeblock will + only ask us to fold the same one again. + */ + printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n"); + return 0xffff; + } + + } + + + /* OK. We now know the location of every block in the Virtual Unit Chain, + and the Erase Unit into which we are supposed to be copying. + Go for it. + */ + + DEBUG(1,"Folding chain %d into unit %d\n", thisVUC, targetEUN); + + for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) { + unsigned char movebuf[512]; + struct nftl_oob oob; + size_t retlen; + + memset(&oob, 0xff, sizeof(oob)); + + /* If it's in the target EUN already, or if it's pending write, do nothing */ + if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) { + /* Except if it's the first block, in which case we have to + set the UnitNumbers */ + if (block == 0) { + + thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , + 16, &retlen, (char *)&oob); + + // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC, + // le16_to_cpu(oob.u.a.VirtUnitNum)); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); + + thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) , + 16, &retlen, (char *)&oob); + } + continue; + } + + oob.b.Status = BLOCK_USED; + + switch(block) { + case 0: + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff); + // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + break; + + case 1: + oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do + oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); + break; + + case 2: + default: + oob.u.c.WriteInh = 0xffffffff; + oob.u.c.unused = 0xffffffff; + } + if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob) == -EIO) { + if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob) != -EIO) + printk("Error went away on retry.\n"); + } + + thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob); + + + /* FIXME: Add some error checking.... */ + thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512), + 16, &retlen, (char *)&oob); + + } + + /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ + + /* At this point, we have two different chains for this Virtual Unit, and no way to tell + them apart. If we crash now, we get confused. However, both contain the same data, so we + shouldn't actually lose data in this case. It's just that when we load up on a medium which + has duplicate chains, we need to free one of the chains because it's not necessary any more. + */ + + + thisEUN = thisNFTL->EUNtable[thisVUC]; + + DEBUG(1,"Want to erase\n"); + /* For each block in the old chain (except the targetEUN of course), + free it and make it available for future use */ + + while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) { + size_t retlen; + struct erase_info *instr; + u16 EUNtmp; + + instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); + if (!instr) { + printk(KERN_WARNING "Out of memory for struct erase_info\n"); + + EUNtmp = thisEUN; + + thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; + thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff; + thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; + } else { + memset(instr, 0, sizeof(struct erase_info)); + instr->addr = thisEUN * thisNFTL->EraseSize; + instr->len = thisNFTL->EraseSize; + + MTD_ERASE(thisNFTL->mtd, instr); + /* This is an async interface. Or will be. At which point + this code will break. */ + +#if 0 + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); + + printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + thisEUN, oob.b.ECCSig[0], + oob.b.ECCSig[1], + oob.b.ECCSig[2], + oob.b.ECCSig[3], + oob.b.ECCSig[4], + oob.b.ECCSig[5]); +#endif + memset(&oob, 0xff, sizeof(oob)); + oob.u.b.WearInfo = cpu_to_le32(3); + oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69); + + MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob); + + EUNtmp = thisEUN; + + thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff; + thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff; + thisNFTL->ReplUnitTable[EUNtmp] = 0xffff; + + thisNFTL->numfreeEUNs++; + + } + + // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + + } + + /* Make this the new start of chain for thisVUC */ + thisNFTL->VirtualUnitTable[targetEUN] = thisVUC; + thisNFTL->ReplUnitTable[targetEUN] = 0xffff; + + thisNFTL->EUNtable[thisVUC] = targetEUN; + return targetEUN; + +} + +u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock) +{ + /* This is the part that needs some cleverness applied. + For now, I'm doing the minimum applicable to actually + get the thing to work. + Wear-levelling and other clever stuff needs to be implemented + and we also need to do some assessment of the results when + the system loses power half-way through the routine. + */ + + u16 LongestChain = 0; + u16 ChainLength = 0, thislen; + u16 chain, EUN; + + + for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) { + EUN = thisNFTL->EUNtable[chain]; + + thislen = 0; + + while (EUN <= thisNFTL->lastEUN) { + thislen++; + // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); + EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff; + if (thislen > 0xff00) { + printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN); + } + if (thislen > 0xff10) { + /* Actually, don't return failure. Just ignore this chain and + get on with it. */ + thislen = 0; + break; + } + + } + + + if (thislen > ChainLength) { + // printk("New longest chain is %d with length %d\n", chain, thislen); + ChainLength = thislen; + LongestChain = chain; + } + } + + if (ChainLength < 2) { + printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n"); + return 0xffff; + } + + return NFTL_foldchain (thisNFTL, LongestChain, pendingblock); +} + +/* NFTL_findwriteunit: Return the unit number into which we can write + for this block. Make it available if it isn't already +*/ + +static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block) +{ + u16 lastEUN; + u16 thisVUC = block / (thisNFTL->EraseSize / 512); + u16 writeEUN; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + size_t retlen; + int silly = 0x10000, silly2 = 3; + struct nftl_oob oob; + int debug=0; + + do { + + /* Scan the media to find a unit in the VUC which has + a free space for the block in question. + */ + + /* This condition catches the 0x[7f]fff cases, as well as + being a sanity check for past-end-of-media access + */ + lastEUN = 0xffff; + writeEUN = thisNFTL->EUNtable[thisVUC]; + + while(writeEUN <= thisNFTL->lastEUN) { + struct nftl_bci bci; + size_t retlen; + + lastEUN = writeEUN; + + MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize) + + blockofs,8, &retlen, (char *)&bci); + + if (debug) + printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status)); + + switch(bci.Status) { + case __constant_cpu_to_le16(BLOCK_FREE): + return writeEUN; + + case __constant_cpu_to_le16(BLOCK_DELETED): + case __constant_cpu_to_le16(BLOCK_USED): + case __constant_cpu_to_le16(BLOCK_IGNORE): + break; + default: + // Invalid block. Don't use it any more. Must implement. + break; + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; + } + + /* Skip to next block in chain */ + + writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff; + } + + /* OK. We didn't find one in the existing chain, or there + is no existing chain. */ + + /* Try to find an already-free block */ + + writeEUN = NFTL_findfreeblock(thisNFTL, 0); + + if (writeEUN == 0xffff) { + /* That didn't work - there were no free blocks just + waiting to be picked up. We're going to have to fold + a chain to make room. + */ + + /* First remember the start of this chain */ + // u16 startEUN = thisNFTL->EUNtable[thisVUC]; + + //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); + writeEUN = NFTL_makefreeblock(thisNFTL, block); + + if (writeEUN == 0xffff) { + /* Ouch. This should never happen - we should + always be able to make some room somehow. + If we get here, we've allocated more storage + space than actual media, or our makefreeblock + routine is missing something. + */ + printk(KERN_WARNING "Cannot make free space.\n"); + return 0xffff; + } + // printk("Restarting scan\n"); + lastEUN = 0xffff; + // debug = 1; + continue; +#if 0 + if (startEUN != thisNFTL->EUNtable[thisVUC]) { + /* The fold operation has moved the chain + that we're looking at. Start the scan again. + */ + continue; + } +#endif + } + + /* We've found a free block. Insert it into the chain. */ + + if (lastEUN != 0xffff) { + /* Addition to an existing chain. Make the previous + last block in the chain point to this one. + */ + + //printk("Linking EUN %d to EUN %d in VUC %d\n", + // lastEUN, writeEUN, thisVUC); + /* Both in our cache... */ + thisNFTL->ReplUnitTable[lastEUN] = writeEUN; + + + /* ... and on the flash itself */ + MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, + (char *)&oob); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN); + + MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen, + (char *)&oob); + + thisVUC |= 0x8000; /* It's a replacement block */ + } else { + /* The first block in a new chain */ + thisNFTL->EUNtable[thisVUC] = writeEUN; + } + + /* Now set up the actual EUN we're writing into */ + + /* Both in our cache... */ + thisNFTL->VirtualUnitTable[writeEUN] = thisVUC; + thisNFTL->ReplUnitTable[writeEUN] = 0xffff; + + /* ... and on the flash itself */ + MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, + &retlen, (char *)&oob); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + + MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16, + &retlen, (char *)&oob); + + return writeEUN; + + } while (silly2--); + + printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC); + return 0xffff; +} + +static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block, + char *buffer) +{ + u16 writeEUN; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + size_t retlen; + u16 eccbuf[8]; + + // if (thisEUN == 0xffff) thisEUN = 0; + + writeEUN = NFTL_findwriteunit(thisNFTL, block); + +// printk("writeblock(%d): Write to Unit %d\n", block, writeEUN); + + if (writeEUN == 0xffff) { + printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n"); + /* If we _still_ haven't got a block to use, we're screwed */ + return 1; + } +// printk("Writing block %lx to EUN %x\n",block, writeEUN); + + + thisNFTL->mtd->write_ecc(thisNFTL->mtd, + (writeEUN * thisNFTL->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)eccbuf); + eccbuf[3] = BLOCK_USED; + eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff; + + thisNFTL->mtd->write_oob(thisNFTL->mtd, + (writeEUN * thisNFTL->EraseSize) + blockofs, + 16, &retlen, (char *)eccbuf); + + return 0; +} + +#endif /* CONFIG_NFTL_RW */ + +static int NFTL_readblock(struct NFTLrecord *thisNFTL, + unsigned block, char *buffer) +{ + u16 lastgoodEUN = 0xffff; + u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1); + + int silly = -1; + + if (thisEUN == 0xffff) thisEUN = 0; + + while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) { + struct nftl_bci bci; + size_t retlen; + + MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci); + + switch(bci.Status) { + case __constant_cpu_to_le16(BLOCK_FREE): + thisEUN = 0; + break; + case __constant_cpu_to_le16(BLOCK_USED): + lastgoodEUN = thisEUN; + break; + case __constant_cpu_to_le16(BLOCK_IGNORE): + case __constant_cpu_to_le16(BLOCK_DELETED): + break; + default: + printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status); + } + + if (!silly--) { + printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512)); + return 1; + } + if (thisEUN) + thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff; + } + if (lastgoodEUN == 0xffff) { + memset(buffer, 0, 512); + } else { + loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs; + size_t retlen; + u_char eccbuf[6]; + thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf); + } + return 0; +} + + +static int nftl_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct NFTLrecord *thisNFTL; + + thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; + + if (!thisNFTL) return -EINVAL; + + + switch (cmd) { + case HDIO_GETGEO: { + struct hd_geometry g; + + g.heads = thisNFTL->heads; + g.sectors = thisNFTL->sectors; + g.cylinders = thisNFTL->cylinders; + g.start = part_table[MINOR(inode->i_rdev)].start_sect; + return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; + } + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, + (long *) arg); + + case BLKFLSBUF: + if(!capable(CAP_SYS_ADMIN)) return -EACCES; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + if (thisNFTL->mtd->sync) + thisNFTL->mtd->sync(thisNFTL->mtd); + return 0; + + case BLKRRPART: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (thisNFTL->usecount > 1) { + // printk("Use count %d\n", thisNFTL->usecount); + return -EBUSY; + } +#if LINUX_VERSION_CODE < 0x20328 + resetup_one_dev(&nftl_gendisk, MINOR(inode->i_dev) / 16); +#else + grok_partitions(&nftl_gendisk, MINOR(inode->i_dev) / 16, 1<<4, thisNFTL->nr_sects); +#endif + return 0; + + // RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ + default: + return -EINVAL; + } +} + + +void nftl_request(RQFUNC_ARG) +{ + unsigned int dev, block, nsect; + struct NFTLrecord *thisNFTL; + char *buffer; + struct request *req; + int res; + + while (1) { + INIT_REQUEST; /* blk.h */ + + req = CURRENT; +#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK + blkdev_dequeue_request(req); + spin_unlock_irq(&io_request_lock); +#else + req = CURRENT; +#endif + + DEBUG(2,"NFTL_request\n"); + DEBUG(3,"NFTL %d request, %lx, %lx", req->cmd, + req->sector, req->current_nr_sectors); + dev = MINOR(req->rq_dev); + block = req->sector; + nsect = req->current_nr_sectors; + buffer = req->buffer; + res = 1; /* succeed */ + + if (dev >= MAX_NFTLS * 16) { + printk("fl: bad minor number: device=%s\n", + kdevname(req->rq_dev)); + res = 0; /* fail */ + goto repeat; + } + + thisNFTL = NFTLs[dev / 16]; + DEBUG(3,"Waiting for mutex\n"); + down(&thisNFTL->mutex); + DEBUG(3,"Got mutex\n"); + + if (block + nsect >= part_table[dev].nr_sects) { + printk("nftl%c%d: bad access: block=%d, count=%d\n", + (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); + up(&thisNFTL->mutex); + res = 0; /* fail */ + goto repeat; + } + + block += part_table[dev].start_sect; + + if (req->cmd == READ) { + DEBUG(2,"NFTL read\n"); + for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { + /* Read a single sector to req->buffer + (512 * i) */ + + if (NFTL_readblock(thisNFTL, block, buffer)) { + DEBUG(2,"NFTL read request failed\n"); + up(&thisNFTL->mutex); + res = 0; + goto repeat; + } + } + DEBUG(2,"NFTL read request completed OK\n"); + up(&thisNFTL->mutex); + goto repeat; + } + else if (req->cmd == WRITE) { + DEBUG(2,"NFTL write request of 0x%x sectors @ %x (req->nr_sectors == %lx\n",nsect, block, req->nr_sectors); +#ifdef CONFIG_NFTL_RW + for ( ; nsect > 0; nsect-- , block++, buffer+= 512) { + /* Read a single sector to req->buffer + (512 * i) */ + + if (NFTL_writeblock(thisNFTL, block, buffer)) { + DEBUG(1,"NFTL write request failed\n"); + + up(&thisNFTL->mutex); + res = 0; + goto repeat; + } + } + DEBUG(2,"NFTL write request completed OK\n"); +#else + res=0; /* Writes always fail */ +#endif /* CONFIG_NFTL_RW */ + up(&thisNFTL->mutex); + goto repeat; + } + else { + DEBUG(0,"NFTL ??? request\n"); + up(&thisNFTL->mutex); + res = 0; + goto repeat; + } + repeat: + DEBUG(3,"end_request(%d)\n", res); +#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK + spin_lock_irq(&io_request_lock); + nftl_end_request(req, res); +#else + end_request(res); +#endif + } +} + +static int nftl_open(struct inode *ip, struct file *fp) +{ + struct NFTLrecord *thisNFTL; + thisNFTL = NFTLs[MINOR(ip->i_rdev) / 16]; + + DEBUG(2,"NFTL_open\n"); + + if (!thisNFTL) { + DEBUG(2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", + MINOR(ip->i_rdev) / 16,ip->i_rdev,ip, fp); + return -ENODEV; + } +#ifndef CONFIG_NFTL_RW + if (fp->f_mode & FMODE_WRITE) + return -EROFS; +#endif /* !CONFIG_NFTL_RW */ + thisNFTL->usecount++; + MOD_INC_USE_COUNT; + if (!get_mtd_device(thisNFTL->mtd, -1)) { + MOD_DEC_USE_COUNT; + return /* -E'SBUGGEREDOFF */ -ENXIO; + } + + return 0; +} + +static int nftl_release(struct inode *inode, struct file *fp) +{ + struct super_block *sb = get_super(inode->i_rdev); + struct NFTLrecord *thisNFTL; + + thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; + + DEBUG(2, "NFTL_release\n"); + + fsync_dev(inode->i_rdev); + if (sb) + invalidate_inodes(sb); + invalidate_buffers(inode->i_rdev); + + if (thisNFTL->mtd->sync) + thisNFTL->mtd->sync(thisNFTL->mtd); + thisNFTL->usecount--; + MOD_DEC_USE_COUNT; + + put_mtd_device(thisNFTL->mtd); + + return 0; +} +#if LINUX_VERSION_CODE < 0x20326 +static struct file_operations nftl_fops = { + NULL, /* lseek - default */ + block_read, /* read - block dev read */ + block_write, /* write - block dev write */ + NULL, /* readdir - not here! */ + NULL, /* select */ + nftl_ioctl, /* ioctl */ + NULL, /* mmap */ + nftl_open, /* open */ + NULL, /* flush */ + nftl_release, /* no special release code... */ + block_fsync /* fsync */ +}; +#else +static struct block_device_operations nftl_fops = +{ + open: nftl_open, + release: nftl_release, + ioctl: nftl_ioctl +}; +#endif + + + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_nftl init_module +#define cleanup_nftl cleanup_module +#endif +#define __exit +#endif + +static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL}; + + +/* static int __init init_nftl(void) */ +int __init init_nftl(void) +{ + int i; + + printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n"); +#ifdef PRERELEASE + printk(KERN_INFO"$Id: nftl.c,v 1.34 2000/06/07 14:48:52 dwmw2 Exp $\n"); +#endif + + if (register_blkdev(NFTL_MAJOR, "nftl", &nftl_fops)){ + printk("unable to register NFTL block device\n"); + } else { +#if LINUX_VERSION_CODE < 0x20320 + blk_dev[MAJOR_NR].request_fn = nftl_request; +#else + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); +#endif + for (i=0; i < 256 ; i++) { + nftl_blocksizes[i] = 1024; + } + blksize_size[NFTL_MAJOR] = nftl_blocksizes; + nftl_gendisk.next = gendisk_head; + gendisk_head = &nftl_gendisk; + } + + register_mtd_user(&nftl_notifier); + + return 0; +} + +static void __exit cleanup_nftl(void) +{ + struct gendisk *gd, **gdp; + + unregister_mtd_user(&nftl_notifier); + + unregister_blkdev(NFTL_MAJOR, "nftl"); +#if LINUX_VERSION_CODE < 0x20320 + blk_dev[MAJOR_NR].request_fn = 0; +#else + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +#endif + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &nftl_gendisk) { + gd = *gdp; *gdp = gd->next; + break; + } + +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_nftl); +module_exit(cleanup_nftl); +#endif diff --git a/drivers/mtd/nora.c b/drivers/mtd/nora.c new file mode 100644 index 000000000..a92e47734 --- /dev/null +++ b/drivers/mtd/nora.c @@ -0,0 +1,208 @@ +/* + * $Id: nora.c,v 1.11 2000/07/04 16:42:50 dwmw2 Exp $ + * + * This is so simple I love it. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> + + +#define WINDOW_ADDR 0xd0000000 +#define WINDOW_SIZE 0x04000000 + +static struct mtd_info *mymtd; + +__u8 nora_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(WINDOW_ADDR + ofs); +} + +__u16 nora_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(WINDOW_ADDR + ofs); +} + +__u32 nora_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(WINDOW_ADDR + ofs); +} + +void nora_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(WINDOW_ADDR + from), len); +} + +void nora_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(WINDOW_ADDR + adr) = d; +} + +void nora_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(WINDOW_ADDR + adr) = d; +} + +void nora_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(WINDOW_ADDR + adr) = d; +} + +void nora_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(WINDOW_ADDR + to), from, len); +} + +struct map_info nora_map = { + "NORA", + WINDOW_SIZE, + 2, + nora_read8, + nora_read16, + nora_read32, + nora_copy_from, + nora_write8, + nora_write16, + nora_write32, + nora_copy_to, + 0, + 0 +}; + + +static int nora_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf); +} + +static int nora_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf); +} + +static int nora_mtd_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + instr->addr += (unsigned long)mtd->priv; + return mymtd->erase(mymtd, instr); +} + +static void nora_mtd_sync (struct mtd_info *mtd) +{ + mymtd->sync(mymtd); +} + +static int nora_mtd_suspend (struct mtd_info *mtd) +{ + return mymtd->suspend(mymtd); +} + +static void nora_mtd_resume (struct mtd_info *mtd) +{ + mymtd->resume(mymtd); +} + + +static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */ + { + type: MTD_NORFLASH, + flags: MTD_CAP_NORFLASH, + size: 0x60000, + erasesize: 0x20000, + name: "NORA boot firmware", + module: THIS_MODULE, + erase: nora_mtd_erase, + read: nora_mtd_read, + write: nora_mtd_write, + suspend: nora_mtd_suspend, + resume: nora_mtd_resume, + sync: nora_mtd_sync, + priv: (void *)0 + }, + { + type: MTD_NORFLASH, + flags: MTD_CAP_NORFLASH, + size: 0x1a0000, + erasesize: 0x20000, + name: "NORA kernel", + module: THIS_MODULE, + erase: nora_mtd_erase, + read: nora_mtd_read, + write: nora_mtd_write, + suspend: nora_mtd_suspend, + resume: nora_mtd_resume, + sync: nora_mtd_sync, + priv: (void *)0x60000 + }, + { + type: MTD_NORFLASH, + flags: MTD_CAP_NORFLASH, + size: 0xe00000, + erasesize: 0x20000, + name: "NORA ramdisk", + module: THIS_MODULE, + erase: nora_mtd_erase, + read: nora_mtd_read, + write: nora_mtd_write, + suspend: nora_mtd_suspend, + resume: nora_mtd_resume, + sync: nora_mtd_sync, + priv: (void *)0x200000 + }, + { + type: MTD_NORFLASH, + flags: MTD_CAP_NORFLASH, + size: 0x1000000, + erasesize: 0x20000, + name: "NORA filesystem", + module: THIS_MODULE, + erase: nora_mtd_erase, + read: nora_mtd_read, + write: nora_mtd_write, + suspend: nora_mtd_suspend, + resume: nora_mtd_resume, + sync: nora_mtd_sync, + priv: (void *)0x1000000 + } +}; + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_nora init_module +#define cleanup_nora cleanup_module +#endif +#endif + +int __init init_nora(void) +{ + printk(KERN_NOTICE "nora flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + + mymtd = do_cfi_probe(&nora_map); + if (mymtd) { +#ifdef MODULE + mymtd->module = &__this_module; +#endif + + add_mtd_device(&nora_mtds[3]); + add_mtd_device(&nora_mtds[0]); + add_mtd_device(&nora_mtds[1]); + add_mtd_device(&nora_mtds[2]); + return 0; + } + + return -ENXIO; +} + +static void __exit cleanup_nora(void) +{ + if (mymtd) { + del_mtd_device(&nora_mtds[2]); + del_mtd_device(&nora_mtds[1]); + del_mtd_device(&nora_mtds[0]); + del_mtd_device(&nora_mtds[3]); + map_destroy(mymtd); + } +} diff --git a/drivers/mtd/octagon-5066.c b/drivers/mtd/octagon-5066.c new file mode 100644 index 000000000..167e0da13 --- /dev/null +++ b/drivers/mtd/octagon-5066.c @@ -0,0 +1,290 @@ +// $Id: octagon-5066.c,v 1.9 2000/07/03 10:01:38 dwmw2 Exp $ +/* ###################################################################### + + Octagon 5066 MTD Driver. + + The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It + comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that + is replacable by flash. Both units are mapped through a multiplexer + into a 32k memory window at 0xe8000. The control register for the + multiplexing unit is located at IO 0x208 with a bit map of + 0-5 Page Selection in 32k increments + 6-7 Device selection: + 00 SSD off + 01 SSD 0 (Socket) + 10 SSD 1 (Flash chip) + 11 undefined + + On each SSD, the first 128k is reserved for use by the bios + (actually it IS the bios..) This only matters if you are booting off the + flash, you must not put a file system starting there. + + The driver tries to do a detection algorithm to guess what sort of devices + are plugged into the sockets. + + ##################################################################### */ + +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/io.h> + +#include <linux/mtd/map.h> + +#define WINDOW_START 0xe8000 +#define WINDOW_LENGTH 0x8000 +#define WINDOW_SHIFT 27 +#define WINDOW_MASK 0x7FFF +#define PAGE_IO 0x208 + +static volatile char page_n_dev = 0; +static unsigned long iomapadr; +static spinlock_t oct5066_spin = SPIN_LOCK_UNLOCKED; + +/* + * We use map_priv_1 to identify which device we are. + */ + +static void __oct5066_page(struct map_info *map, __u8 byte) +{ + outb(byte,PAGE_IO); + page_n_dev = byte; +} + +static inline void oct5066_page(struct map_info *map, unsigned long ofs) +{ + __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT); + + if (page_n_dev != byte); + __oct5066_page(map, byte); +} + + +static __u8 oct5066_read8(struct map_info *map, unsigned long ofs) +{ + __u8 ret; + spin_lock(&oct5066_spin); + oct5066_page(map, ofs); + ret = readb(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&oct5066_spin); + return ret; +} + +static __u16 oct5066_read16(struct map_info *map, unsigned long ofs) +{ + __u16 ret; + spin_lock(&oct5066_spin); + oct5066_page(map, ofs); + ret = readw(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&oct5066_spin); + return ret; +} + +static __u32 oct5066_read32(struct map_info *map, unsigned long ofs) +{ + __u32 ret; + spin_lock(&oct5066_spin); + oct5066_page(map, ofs); + ret = readl(iomapadr + (ofs & WINDOW_MASK)); + spin_unlock(&oct5066_spin); + return ret; +} + +static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(from & WINDOW_MASK); + + spin_lock(&oct5066_spin); + oct5066_page(map, from); + memcpy_fromio(to, iomapadr + from, thislen); + spin_unlock(&oct5066_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + spin_lock(&oct5066_spin); + oct5066_page(map, adr); + writeb(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&oct5066_spin); +} + +static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + spin_lock(&oct5066_spin); + oct5066_page(map, adr); + writew(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&oct5066_spin); +} + +static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + spin_lock(&oct5066_spin); + oct5066_page(map, adr); + writel(d, iomapadr + (adr & WINDOW_MASK)); + spin_unlock(&oct5066_spin); +} + +static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(to & WINDOW_MASK); + + spin_lock(&oct5066_spin); + oct5066_page(map, to); + memcpy_toio(iomapadr + to, from, thislen); + spin_unlock(&oct5066_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static struct map_info oct5066_map[2] = { + { + "Octagon 5066 Socket", + 512 * 1024, + 1, + oct5066_read8, + oct5066_read16, + oct5066_read32, + oct5066_copy_from, + oct5066_write8, + oct5066_write16, + oct5066_write32, + oct5066_copy_to, + 1<<6 + }, + { + "Octagon 5066 Internal Flash", + 2 * 1024 * 1024, + 1, + oct5066_read8, + oct5066_read16, + oct5066_read32, + oct5066_copy_from, + oct5066_write8, + oct5066_write16, + oct5066_write32, + oct5066_copy_to, + 2<<6 + } +}; + +static struct mtd_info *oct5066_mtd[2] = {NULL, NULL}; + +// OctProbe - Sense if this is an octagon card +// --------------------------------------------------------------------- +/* Perform a simple validity test, we map the window select SSD0 and + change pages while monitoring the window. A change in the window, + controlled by the PAGE_IO port is a functioning 5066 board. This will + fail if the thing in the socket is set to a uniform value. */ +static int __init OctProbe() +{ + unsigned int Base = (1 << 6); + unsigned long I; + unsigned long Values[10]; + for (I = 0; I != 20; I++) + { + outb(Base + (I%10),PAGE_IO); + if (I < 10) + { + // Record the value and check for uniqueness + Values[I%10] = readl(iomapadr); + if (I > 0 && Values[I%10] == Values[0]) + return -EAGAIN; + } + else + { + // Make sure we get the same values on the second pass + if (Values[I%10] != readl(iomapadr)) + return -EAGAIN; + } + } + return 0; +} + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_oct5066 init_module +#define cleanup_oct5066 cleanup_module +#endif +#define __exit +#endif + +void cleanup_oct5066(void) +{ + int i; + for (i=0; i<2; i++) { + if (oct5066_mtd[i]) { + del_mtd_device(oct5066_mtd[i]); + map_destroy(oct5066_mtd[i]); + } + } + iounmap((void *)iomapadr); + release_region(PAGE_IO,1); +} + +int __init init_oct5066(void) +{ + int i; + + // Do an autoprobe sequence + if (check_region(PAGE_IO,1) != 0) + { + printk("5066: Page Register in Use\n"); + return -EAGAIN; + } + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk("Failed to ioremap memory region\n"); + return -EIO; + } + if (OctProbe() != 0) + { + printk("5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); + iounmap((void *)iomapadr); + return -EAGAIN; + } + + request_region(PAGE_IO,1,"Octagon SSD"); + + // Print out our little header.. + printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START, + WINDOW_START+WINDOW_LENGTH); + + for (i=0; i<2; i++) { + oct5066_mtd[i] = do_cfi_probe(&oct5066_map[i]); + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_jedec_probe(&oct5066_map[i]); + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_ram_probe(&oct5066_map[i]); + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_rom_probe(&oct5066_map[i]); + if (oct5066_mtd[i]) { + oct5066_mtd[i]->module = THIS_MODULE; + add_mtd_device(oct5066_mtd[i]); + } + } + + if (!oct5066_mtd[1] && !oct5066_mtd[2]) { + cleanup_oct5066(); + return -ENXIO; + } + + return 0; +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_oct5066); +module_exit(cleanup_oct5066); +#endif diff --git a/drivers/mtd/physmap.c b/drivers/mtd/physmap.c new file mode 100644 index 000000000..e470a6c97 --- /dev/null +++ b/drivers/mtd/physmap.c @@ -0,0 +1,114 @@ +/* + * $Id: physmap.c,v 1.1 2000/07/04 08:58:10 dwmw2 Exp $ + * + * Normal mappings of chips in physical memory + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> + + +#define WINDOW_ADDR 0x8000000 +#define WINDOW_SIZE 0x4000000 + +static struct mtd_info *mymtd; + +__u8 physmap_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +__u16 physmap_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +__u32 physmap_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info physmap_map = { + "Physically mapped flash", + WINDOW_SIZE, + 2, + physmap_read8, + physmap_read16, + physmap_read32, + physmap_copy_from, + physmap_write8, + physmap_write16, + physmap_write32, + physmap_copy_to, + 0, + 0 +}; + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_physmap init_module +#define cleanup_physmap cleanup_module +#endif +#endif + +int __init init_physmap(void) +{ + printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_SIZE, WINDOW_ADDR); + + if (!physmap_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_cfi_probe(&physmap_map); + if (mymtd) { +#ifdef MODULE + mymtd->module = &__this_module; +#endif + add_mtd_device(mymtd); + return 0; + } + + return -ENXIO; +} + +static void __exit cleanup_physmap(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (physmap_map.map_priv_1) { + iounmap((void *)physmap_map.map_priv_1); + physmap_map.map_priv_1 = 0; + } +} diff --git a/drivers/mtd/pmc551.c b/drivers/mtd/pmc551.c new file mode 100644 index 000000000..98d5c3069 --- /dev/null +++ b/drivers/mtd/pmc551.c @@ -0,0 +1,689 @@ +/* + * $Id: pmc551.c,v 1.7 2000/07/03 10:01:38 dwmw2 Exp $ + * + * PMC551 PCI Mezzanine Ram Device + * + * Author: + * Mark Ferrell + * Copyright 1999,2000 Nortel Networks + * + * License: + * As part of this driver was derrived from the slram.c driver it falls + * under the same license, which is GNU General Public License v2 + * + * Description: + * This driver is intended to support the PMC551 PCI Ram device from + * Ramix Inc. The PMC551 is a PMC Mezzanine module for cPCI embeded + * systems. The device contains a single SROM that initally programs the + * V370PDC chipset onboard the device, and various banks of DRAM/SDRAM + * onboard. This driver implements this PCI Ram device as an MTD (Memory + * Technologies Device) so that it can be used to hold a filesystem, or + * for added swap space in embeded systems. Since the memory on this + * board isn't as fast as main memory we do not try to hook it into main + * memeory as that would simply reduce performance on the system. Using + * it as a block device allows us to use it as high speed swap or for a + * high speed disk device of some sort. Which becomes very usefull on + * diskless systems in the embeded market I might add. + * + * Credits: + * Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the initial + * example code of how to initialize this device and for help with + * questions I had concerning operation of the device. + * + * Most of the MTD code for this driver was originally written for the + * slram.o module in the MTD drivers package written by David Hinds + * <dhinds@allegro.stanford.edu> which allows the mapping of system + * memory into an mtd device. Since the PMC551 memory module is + * accessed in the same fashion as system memory, the slram.c code + * became a very nice fit to the needs of this driver. All we added was + * PCI detection/initialization to the driver and automaticly figure out + * the size via the PCI detection.o, later changes by Corey Minyard + * settup the card to utilize a 1M sliding apature. + * + * Corey Minyard <minyard@nortelnetworks.com> + * * Modified driver to utilize a sliding apature instead of mapping all + * memory into kernel space which turned out to be very wastefull. + * * Located a bug in the SROM's initialization sequence that made the + * memory unussable, added a fix to code to touch up the DRAM some. + * + * Bugs/FIXME's: + * * MUST fix the init function to not spin on a register + * waiting for it to set .. this does not safely handle busted devices + * that never reset the register correctly which will cause the system to + * hang w/ a reboot beeing the only chance at recover. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/uaccess.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <stdarg.h> +#include <linux/pci.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/pmc551.h> +#include <linux/mtd/compatmac.h> + +#if LINUX_VERSION_CODE > 0x20300 +#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start) +#else +#define PCI_BASE_ADDRESS(dev) (dev->base_address[0]) +#endif + +static struct mtd_info *pmc551list = NULL; + +static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct mypriv *priv = mtd->priv; + u32 start_addr_highbits; + u32 end_addr_highbits; + u32 start_addr_lowbits; + u32 end_addr_lowbits; + unsigned long end; + + end = instr->addr + instr->len; + + /* Is it too much memory? The second check find if we wrap around + past the end of a u32. */ + if ((end > mtd->size) || (end < instr->addr)) { + return -EINVAL; + } + + start_addr_highbits = instr->addr & PMC551_ADDR_HIGH_MASK; + end_addr_highbits = end & PMC551_ADDR_HIGH_MASK; + start_addr_lowbits = instr->addr & PMC551_ADDR_LOW_MASK; + end_addr_lowbits = end & PMC551_ADDR_LOW_MASK; + + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + (priv->mem_map0_base_val + | start_addr_highbits)); + if (start_addr_highbits == end_addr_highbits) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memset(priv->start + start_addr_lowbits, + 0xff, + instr->len); + } else { + /* We have to do multiple writes to get all the data + written. */ + memset(priv->start + start_addr_lowbits, + 0xff, + priv->aperture_size - start_addr_lowbits); + start_addr_highbits += priv->aperture_size; + while (start_addr_highbits != end_addr_highbits) { + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + (priv->mem_map0_base_val + | start_addr_highbits)); + memset(priv->start, + 0xff, + priv->aperture_size); + start_addr_highbits += priv->aperture_size; + } + priv->curr_mem_map0_val = (priv->mem_map0_base_val + | start_addr_highbits); + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + memset(priv->start, + 0xff, + end_addr_lowbits); + } + + instr->state = MTD_ERASE_DONE; + + if (instr->callback) { + (*(instr->callback))(instr); + } + + return 0; +} + + +static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr) +{} + + +static int pmc551_read (struct mtd_info *mtd, + loff_t from, + size_t len, + size_t *retlen, + u_char *buf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + u32 start_addr_highbits; + u32 end_addr_highbits; + u32 start_addr_lowbits; + u32 end_addr_lowbits; + unsigned long end; + u_char *copyto = buf; + + + /* Is it past the end? */ + if (from > mtd->size) { + return -EINVAL; + } + + end = from + len; + start_addr_highbits = from & PMC551_ADDR_HIGH_MASK; + end_addr_highbits = end & PMC551_ADDR_HIGH_MASK; + start_addr_lowbits = from & PMC551_ADDR_LOW_MASK; + end_addr_lowbits = end & PMC551_ADDR_LOW_MASK; + + + /* Only rewrite the first value if it doesn't match our current + values. Most operations are on the same page as the previous + value, so this is a pretty good optimization. */ + if (priv->curr_mem_map0_val != + (priv->mem_map0_base_val | start_addr_highbits)) { + priv->curr_mem_map0_val = (priv->mem_map0_base_val + | start_addr_highbits); + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + } + + if (start_addr_highbits == end_addr_highbits) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memcpy(copyto, + priv->start + start_addr_lowbits, + len); + copyto += len; + } else { + /* We have to do multiple writes to get all the data + written. */ + memcpy(copyto, + priv->start + start_addr_lowbits, + priv->aperture_size - start_addr_lowbits); + copyto += priv->aperture_size - start_addr_lowbits; + start_addr_highbits += priv->aperture_size; + while (start_addr_highbits != end_addr_highbits) { + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + (priv->mem_map0_base_val + | start_addr_highbits)); + memcpy(copyto, + priv->start, + priv->aperture_size); + copyto += priv->aperture_size; + start_addr_highbits += priv->aperture_size; + if (start_addr_highbits >= mtd->size) { + /* Make sure we have the right value here. */ + priv->curr_mem_map0_val + = (priv->mem_map0_base_val + | start_addr_highbits); + goto out; + } + } + priv->curr_mem_map0_val = (priv->mem_map0_base_val + | start_addr_highbits); + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + memcpy(copyto, + priv->start, + end_addr_lowbits); + copyto += end_addr_lowbits; + } + +out: + *retlen = copyto - buf; + return 0; +} + +static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + u32 start_addr_highbits; + u32 end_addr_highbits; + u32 start_addr_lowbits; + u32 end_addr_lowbits; + unsigned long end; + const u_char *copyfrom = buf; + + + /* Is it past the end? */ + if (to > mtd->size) { + return -EINVAL; + } + + end = to + len; + start_addr_highbits = to & PMC551_ADDR_HIGH_MASK; + end_addr_highbits = end & PMC551_ADDR_HIGH_MASK; + start_addr_lowbits = to & PMC551_ADDR_LOW_MASK; + end_addr_lowbits = end & PMC551_ADDR_LOW_MASK; + + + /* Only rewrite the first value if it doesn't match our current + values. Most operations are on the same page as the previous + value, so this is a pretty good optimization. */ + if (priv->curr_mem_map0_val != + (priv->mem_map0_base_val | start_addr_highbits)) { + priv->curr_mem_map0_val = (priv->mem_map0_base_val + | start_addr_highbits); + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + } + + if (start_addr_highbits == end_addr_highbits) { + /* The whole thing fits within one access, so just one shot + will do it. */ + memcpy(priv->start + start_addr_lowbits, + copyfrom, + len); + copyfrom += len; + } else { + /* We have to do multiple writes to get all the data + written. */ + memcpy(priv->start + start_addr_lowbits, + copyfrom, + priv->aperture_size - start_addr_lowbits); + copyfrom += priv->aperture_size - start_addr_lowbits; + start_addr_highbits += priv->aperture_size; + while (start_addr_highbits != end_addr_highbits) { + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + (priv->mem_map0_base_val + | start_addr_highbits)); + memcpy(priv->start, + copyfrom, + priv->aperture_size); + copyfrom += priv->aperture_size; + start_addr_highbits += priv->aperture_size; + if (start_addr_highbits >= mtd->size) { + /* Make sure we have the right value here. */ + priv->curr_mem_map0_val + = (priv->mem_map0_base_val + | start_addr_highbits); + goto out; + } + } + priv->curr_mem_map0_val = (priv->mem_map0_base_val + | start_addr_highbits); + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + memcpy(priv->start, + copyfrom, + end_addr_lowbits); + copyfrom += end_addr_lowbits; + } + +out: + *retlen = copyfrom - buf; + return 0; +} + +/* + * Fixup routines for the V370PDC + * PCI device ID 0x020011b0 + * + * This function basicly kick starts the DRAM oboard the card and gets it + * ready to be used. Before this is done the device reads VERY erratic, so + * much that it can crash the Linux 2.2.x series kernels when a user cat's + * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL + * register. FIXME: stop spinning on registers .. must implement a timeout + * mechanism + * returns the size of the memory region found. + */ +static u32 fixup_pmc551 (struct pci_dev *dev) +{ +#ifdef PMC551_DRAM_BUG + u32 dram_data; +#endif + u32 size, dcmd; + u16 cmd, i; + + /* Sanity Check */ + if(!dev) { + return -ENODEV; + } + + /* + * Get the size of the memory by reading all the DRAM size values + * and adding them up. + * + * KLUDGE ALERT: the boards we are using have invalid column and + * row mux values. We fix them here, but this will break other + * memory configurations. + */ +#ifdef PMC551_DRAM_BUG + pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data); + size = PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data); + + pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data); + size += PMC551_DRAM_BLK_GET_SIZE(dram_data); + dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); + dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); + pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data); +#endif /* PMC551_DRAM_BUG */ + + /* + * Oops .. something went wrong + */ + if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) { + return -ENODEV; + } + + /* + * Set to be prefetchable + */ + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd ); + dcmd |= 0x8; + + /* + * Put it back the way it was + */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dcmd ); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd ); + + /* + * Some screen fun + */ + printk(KERN_NOTICE "pmc551: %dM (0x%x) of %sprefetchable memory at 0x%lx\n", + size/1024/1024, size, ((dcmd&0x8) == 0)?"non-":"", + PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK ); + + /* + * Turn on PCI memory and I/O bus access just for kicks + */ + pci_write_config_word( dev, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_IO ); + + /* + * Config DRAM + */ + pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 ); + pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf ); + + /* + * Wait untill command has gone through + * FIXME: register spinning issue + */ + do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd ); + } while ( (PCI_COMMAND_IO) & cmd ); + + /* + * Must be held high for some duration of time to take effect?? + */ + for ( i = 1; i<=8 ; i++) { + pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df); + + /* + * Make certain command has gone through + * FIXME: register spinning issue + */ + do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); + } while ( (PCI_COMMAND_IO) & cmd ); + } + + pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020); + pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff); + + /* + * Wait until command completes + * FIXME: register spinning issue + */ + do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd); + } while ( (PCI_COMMAND_IO) & cmd ); + + pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd); + dcmd |= 0x02000000; + pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd); + + /* + * Check to make certain fast back-to-back, if not + * then set it so + */ + pci_read_config_word( dev, PCI_STATUS, &cmd); + if((cmd&PCI_COMMAND_FAST_BACK) == 0) { + cmd |= PCI_COMMAND_FAST_BACK; + pci_write_config_word( dev, PCI_STATUS, cmd); + } + + /* + * Check to make certain the DEVSEL is set correctly, this device + * has a tendancy to assert DEVSEL and TRDY when a write is performed + * to the memory when memory is read-only + */ + if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) { + cmd &= ~PCI_STATUS_DEVSEL_MASK; + pci_write_config_word( dev, PCI_STATUS, cmd ); + } + + /* + * Check to see the state of the memory + * FIXME: perhaps hide some of this around an #ifdef DEBUG as + * it doesn't effect or enhance cards functionality + */ + pci_read_config_dword( dev, 0x74, &dcmd ); + printk(KERN_NOTICE "pmc551: DRAM_BLK3 Flags: %s,%s\n", + ((0x2&dcmd) == 0)?"RW":"RO", + ((0x1&dcmd) == 0)?"Off":"On" ); + + pci_read_config_dword( dev, 0x70, &dcmd ); + printk(KERN_NOTICE "pmc551: DRAM_BLK2 Flags: %s,%s\n", + ((0x2&dcmd) == 0)?"RW":"RO", + ((0x1&dcmd) == 0)?"Off":"On" ); + + pci_read_config_dword( dev, 0x6C, &dcmd ); + printk(KERN_NOTICE "pmc551: DRAM_BLK1 Flags: %s,%s\n", + ((0x2&dcmd) == 0)?"RW":"RO", + ((0x1&dcmd) == 0)?"Off":"On" ); + + pci_read_config_dword( dev, 0x68, &dcmd ); + printk(KERN_NOTICE "pmc551: DRAM_BLK0 Flags: %s,%s\n", + ((0x2&dcmd) == 0)?"RW":"RO", + ((0x1&dcmd) == 0)?"Off":"On" ); + + pci_read_config_word( dev, 0x4, &cmd ); + printk( KERN_NOTICE "pmc551: Memory Access %s\n", + ((0x2&cmd) == 0)?"off":"on" ); + printk( KERN_NOTICE "pmc551: I/O Access %s\n", + ((0x1&cmd) == 0)?"off":"on" ); + + pci_read_config_word( dev, 0x6, &cmd ); + printk( KERN_NOTICE "pmc551: Devsel %s\n", + ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast": + ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium": + ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" ); + + printk( KERN_NOTICE "pmc551: %sFast Back-to-Back\n", + ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" ); + + return size; +} + +/* + * Kernel version specific module stuffages + */ +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_pmc551 init_module +#define cleanup_pmc551 cleanup_module +#endif +#define __exit +#endif + + +/* + * PMC551 Card Initialization + */ +//static int __init init_pmc551(void) +int __init init_pmc551(void) +{ + struct pci_dev *PCI_Device = NULL; + struct mypriv *priv; + int count, found=0; + struct mtd_info *mtd; + u32 length = 0; + + + printk(KERN_NOTICE "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n"); + printk(KERN_INFO "$Id: pmc551.c,v 1.7 2000/07/03 10:01:38 dwmw2 Exp $\n"); + + if(!pci_present()) { + printk(KERN_NOTICE "pmc551: PCI not enabled.\n"); + return -ENODEV; + } + + /* + * PCU-bus chipset probe. + */ + for( count = 0; count < MAX_MTD_DEVICES; count++ ) { + + if ( (PCI_Device = pci_find_device( PCI_VENDOR_ID_V3_SEMI, + PCI_DEVICE_ID_V3_SEMI_V370PDC, PCI_Device ) ) == NULL) { + break; + } + + printk(KERN_NOTICE "pmc551: Found PCI V370PDC IRQ:%d\n", + PCI_Device->irq); + + /* + * The PMC551 device acts VERY wierd if you don't init it + * first. i.e. it will not correctly report devsel. If for + * some reason the sdram is in a wrote-protected state the + * device will DEVSEL when it is written to causing problems + * with the oldproc.c driver in + * some kernels (2.2.*) + */ + if((length = fixup_pmc551(PCI_Device)) <= 0) { + printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n"); + break; + } + + mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd) { + printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); + break; + } + + memset(mtd, 0, sizeof(struct mtd_info)); + + priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL); + if (!priv) { + printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); + kfree(mtd); + break; + } + memset(priv, 0, sizeof(*priv)); + mtd->priv = priv; + + priv->dev = PCI_Device; + priv->aperture_size = PMC551_APERTURE_SIZE; + priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device) + & PCI_BASE_ADDRESS_MEM_MASK), + priv->aperture_size); + priv->mem_map0_base_val = (PMC551_APERTURE_VAL + | PMC551_PCI_MEM_MAP_REG_EN + | PMC551_PCI_MEM_MAP_ENABLE); + priv->curr_mem_map0_val = priv->mem_map0_base_val; + + pci_write_config_dword ( priv->dev, + PMC551_PCI_MEM_MAP0, + priv->curr_mem_map0_val); + + mtd->size = length; + mtd->flags = (MTD_CLEAR_BITS + | MTD_SET_BITS + | MTD_WRITEB_WRITEABLE + | MTD_VOLATILE); + mtd->erase = pmc551_erase; + mtd->point = NULL; + mtd->unpoint = pmc551_unpoint; + mtd->read = pmc551_read; + mtd->write = pmc551_write; + mtd->module = THIS_MODULE; + mtd->type = MTD_RAM; + mtd->name = "PMC551 RAM board"; + mtd->erasesize = 0x10000; + + if (add_mtd_device(mtd)) { + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); + kfree(mtd->priv); + kfree(mtd); + break; + } + printk(KERN_NOTICE "Registered pmc551 memory device.\n"); + printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", + priv->aperture_size/1024/1024, + priv->start, + priv->start + priv->aperture_size); + printk(KERN_NOTICE "Total memory is %dM\n", length/1024/1024); + priv->nextpmc551 = pmc551list; + pmc551list = mtd; + found++; + } + + if( !pmc551list ) { + printk(KERN_NOTICE "pmc551: not detected,\n"); + return -ENODEV; + } else { + return 0; + printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found); + } +} + +/* + * PMC551 Card Cleanup + */ +static void __exit cleanup_pmc551(void) +{ + int found=0; + struct mtd_info *mtd; + struct mypriv *priv; + + while((mtd=pmc551list)) { + priv = (struct mypriv *)mtd->priv; + pmc551list = priv->nextpmc551; + + if(priv->start) + iounmap(((struct mypriv *)mtd->priv)->start); + + kfree (mtd->priv); + del_mtd_device(mtd); + kfree(mtd); + found++; + } + + printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found); +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_pmc551); +module_exit(cleanup_pmc551); +#endif + + + diff --git a/drivers/mtd/rpxlite.c b/drivers/mtd/rpxlite.c new file mode 100644 index 000000000..783c863ac --- /dev/null +++ b/drivers/mtd/rpxlite.c @@ -0,0 +1,160 @@ +/* + * $Id: rpxlite.c,v 1.2 2000/07/04 12:16:26 dwmw2 Exp $ + * + * Handle the strange 16-in-32-bit mapping on the RPXLite board + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> + + +#define WINDOW_ADDR 0x8000000 +#define WINDOW_SIZE 0x2000000 + +#define MAP_TO_ADR(x) ( ( ( x & ~1 ) << 1 ) | (x&1) ) + +static struct mtd_info *mymtd; + +__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + MAP_TO_ADR(ofs)); +} + +__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + MAP_TO_ADR(ofs)); +} + +__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + MAP_TO_ADR(ofs)); +} + +void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + if (from & 1) { + *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from)); + from++; + len--; + } + /* Can't do this if it's not aligned */ + if (!((unsigned long)to & 1)) { + unsigned long fromadr = MAP_TO_ADR(from); + + while (len > 1) { + *(__u16 *)to = readw(map->map_priv_1 + fromadr); + to += 2; + fromadr += 4; + from += 2; + len -= 2; + } + } + while(len) { + *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from)); + to++; + from++; + len--; + } +} + +void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + MAP_TO_ADR(adr)); +} + +void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + MAP_TO_ADR(adr)); +} + +void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + MAP_TO_ADR(adr)); +} + +void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + if (to & 1) { + writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to)); + from++; + len--; + } + /* Can't do this if it's not aligned */ + if (!((unsigned long)from & 1)) { + unsigned long toadr = map->map_priv_1 + MAP_TO_ADR(to); + + while (len > 1) { + writew(*(__u16 *)from, toadr); + from += 2; + toadr += 4; + to += 2; + len -= 2; + } + } + while(len) { + writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to)); + to++; + from++; + len--; + } +} + +struct map_info rpxlite_map = { + "RPXLITE", + WINDOW_SIZE, + 2, + rpxlite_read8, + rpxlite_read16, + rpxlite_read32, + rpxlite_copy_from, + rpxlite_write8, + rpxlite_write16, + rpxlite_write32, + rpxlite_copy_to, + 0, + 0 +}; + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_rpxlite init_module +#define cleanup_rpxlite cleanup_module +#endif +#endif + +int __init init_rpxlite(void) +{ + printk(KERN_NOTICE "rpxlite flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 2); + + if (!rpxlite_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_cfi_probe(&rpxlite_map); + if (mymtd) { +#ifdef MODULE + mymtd->module = &__this_module; +#endif + add_mtd_device(mymtd); + return 0; + } + + return -ENXIO; +} + +static void __exit cleanup_rpxlite(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (rpxlite_map.map_priv_1) { + iounmap((void *)rpxlite_map.map_priv_1); + rpxlite_map.map_priv_1 = 0; + } +} diff --git a/drivers/mtd/slram.c b/drivers/mtd/slram.c new file mode 100644 index 000000000..48067c814 --- /dev/null +++ b/drivers/mtd/slram.c @@ -0,0 +1,226 @@ +/*====================================================================== + + $Id: slram.c,v 1.10 2000/07/03 10:01:38 dwmw2 Exp $ + +======================================================================*/ + + +#include <linux/module.h> +#include <asm/uaccess.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/major.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <stdarg.h> + +#include <linux/mtd/mtd.h> + +struct mypriv { + u_char *start; + u_char *end; +}; + +int physmem_erase (struct mtd_info *mtd, struct erase_info *instr); +int physmem_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); +void physmem_unpoint (struct mtd_info *mtd, u_char *addr); +int physmem_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +int physmem_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + + +int physmem_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct mypriv *priv = mtd->priv; + + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + memset(priv->start + instr->addr, 0xff, instr->len); + + /* This'll catch a few races. Free the thing before returning :) + * I don't feel at all ashamed. This kind of thing is possible anyway + * with flash, but unlikely. + */ + + instr->state = MTD_ERASE_DONE; + + if (instr->callback) + (*(instr->callback))(instr); + else + kfree(instr); + + return 0; +} + + +int physmem_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + + *mtdbuf = priv->start + from; + *retlen = len; + return 0; +} + +void physmem_unpoint (struct mtd_info *mtd, u_char *addr) +{ +} + +int physmem_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + + memcpy (buf, priv->start + from, len); + + *retlen=len; + return 0; +} + +int physmem_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct mypriv *priv = (struct mypriv *)mtd->priv; + + memcpy (priv->start + to, buf, len); + + *retlen=len; + return 0; +} + + + + +/*====================================================================*/ + +/* Place your defaults here */ + +static u_long start = 100663296; +static u_long length = 33554432; +static u_long end = 0; + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_slram init_module +#define cleanup_slram cleanup_module +#endif +#define __exit +#endif + +#ifdef MODULE +MODULE_PARM(start,"l"); +MODULE_PARM(length,"l"); +MODULE_PARM(end,"l"); +#endif + +struct mtd_info *mymtd; + +void __init mtd_slram_setup(char *str, int *ints) +{ + if (ints[0] > 0) + start=ints[1]; + if (ints[0] > 1) + length=ints[2]; +} + +int init_slram(void) +{ + if (!start) + { + printk(KERN_NOTICE "physmem: No start address for memory device.\n"); + return -EINVAL; + } + + if (!length && !end) + { + printk(KERN_NOTICE "physmem: No length or endpointer given.\n"); + return -EINVAL; + } + + if (!end) + end = start + length; + + if (!length) + length = end - start; + + if (start + length != end) + { + printk(KERN_NOTICE "physmem: start(%lx) + length(%lx) != end(%lx) !\n", + start, length, end); + return -EINVAL; + } + + mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + + memset(mymtd, 0, sizeof(*mymtd)); + + if (mymtd) + { + memset((char *)mymtd, 0, sizeof(struct mtd_info)); + mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL); + if (!mymtd->priv) + { + kfree(mymtd); + mymtd = NULL; + } + memset(mymtd->priv, 0, sizeof(struct mypriv)); + } + + if (!mymtd) + { + printk(KERN_NOTICE "physmem: Cannot allocate new MTD device.\n"); + return -ENOMEM; + } + + + ((struct mypriv *)mymtd->priv)->start = ioremap(start, length); + ((struct mypriv *)mymtd->priv)->end = ((struct mypriv *)mymtd->priv)->start + length; + + + mymtd->name = "Raw memory"; + + mymtd->size = length; + mymtd->flags = MTD_CLEAR_BITS | MTD_SET_BITS | MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + mymtd->erase = physmem_erase; + mymtd->point = physmem_point; + mymtd->unpoint = physmem_unpoint; + mymtd->read = physmem_read; + mymtd->write = physmem_write; + mymtd->module = THIS_MODULE; + mymtd->type = MTD_RAM; + mymtd->erasesize = 0x10000; + + if (add_mtd_device(mymtd)) + { + printk("Failed to register new device\n"); + kfree(mymtd->priv); + kfree(mymtd); + return -EAGAIN; + } + printk("Registered physmem device from %dKb to %dKb\n", + (int)(start / 1024), (int)(end / 1024)); + printk("Mapped from 0x%p to 0x%p\n",((struct mypriv *)mymtd->priv)->start, +((struct mypriv *)mymtd->priv)->end); + + return 0; +} + +static void __exit cleanup_slram(void) +{ + iounmap(((struct mypriv *)mymtd->priv)->start); + kfree (mymtd->priv); + del_mtd_device(mymtd); + kfree(mymtd); +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_slram); +module_exit(cleanup_slram); +#endif diff --git a/drivers/mtd/vmax301.c b/drivers/mtd/vmax301.c new file mode 100644 index 000000000..553beaad6 --- /dev/null +++ b/drivers/mtd/vmax301.c @@ -0,0 +1,243 @@ +// $Id: vmax301.c,v 1.13 2000/07/03 10:01:38 dwmw2 Exp $ +/* ###################################################################### + + Tempustech VMAX SBC301 MTD Driver. + + The VMAx 301 is a SBC based on . It + comes with three builtin AMD 29F016B flash chips and a socket for SRAM or + more flash. Each unit has it's own 8k mapping into a settable region + (0xD8000). There are two 8k mappings for each MTD, the first is always set + to the lower 8k of the device the second is paged. Writing a 16 bit page + value to anywhere in the first 8k will cause the second 8k to page around. + + To boot the device a bios extension must be installed into the first 8k + of flash that is smart enough to copy itself down, page in the rest of + itself and begin executing. + + ##################################################################### */ + +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/spinlock.h> +#include <asm/io.h> + +#include <linux/mtd/map.h> + + +#define WINDOW_START 0xd8000 +#define WINDOW_LENGTH 0x2000 +#define WINDOW_SHIFT 25 +#define WINDOW_MASK 0x1FFF + +/* Actually we could use two spinlocks, but we'd have to have + more private space in the struct map_info. We lose a little + performance like this, but we'd probably lose more by having + the extra indirection from having one of the map->map_priv + fields pointing to yet another private struct. +*/ +static spinlock_t vmax301_spin = SPIN_LOCK_UNLOCKED; + +static void __vmax301_page(struct map_info *map, unsigned long page) +{ + writew(page, map->map_priv_2 - WINDOW_LENGTH); + map->map_priv_1 = page; +} + +static inline void vmax301_page(struct map_info *map, + unsigned long ofs) +{ + unsigned long page = (ofs >> WINDOW_SHIFT); + if (map->map_priv_1 != page) + __vmax301_page(map, page); +} + +static __u8 vmax301_read8(struct map_info *map, unsigned long ofs) +{ + __u8 ret; + spin_lock(&vmax301_spin); + vmax301_page(map, ofs); + ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); + spin_unlock(&vmax301_spin); + return ret; +} + +static __u16 vmax301_read16(struct map_info *map, unsigned long ofs) +{ + __u16 ret; + spin_lock(&vmax301_spin); + vmax301_page(map, ofs); + ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK)); + spin_unlock(&vmax301_spin); + return ret; +} + +static __u32 vmax301_read32(struct map_info *map, unsigned long ofs) +{ + __u32 ret; + spin_lock(&vmax301_spin); + vmax301_page(map, ofs); + ret = readl(map->map_priv_2 + (ofs & WINDOW_MASK)); + spin_unlock(&vmax301_spin); + return ret; +} + +static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(from & WINDOW_MASK); + spin_lock(&vmax301_spin); + vmax301_page(map, from); + memcpy_fromio(to, map->map_priv_2 + from, thislen); + spin_unlock(&vmax301_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + spin_lock(&vmax301_spin); + vmax301_page(map, adr); + writeb(d, map->map_priv_2 + (adr & WINDOW_MASK)); + spin_unlock(&vmax301_spin); +} + +static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + spin_lock(&vmax301_spin); + vmax301_page(map, adr); + writew(d, map->map_priv_2 + (adr & WINDOW_MASK)); + spin_unlock(&vmax301_spin); +} + +static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + spin_lock(&vmax301_spin); + vmax301_page(map, adr); + writel(d, map->map_priv_2 + (adr & WINDOW_MASK)); + spin_unlock(&vmax301_spin); +} + +static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while(len) { + unsigned long thislen = len; + if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) + thislen = WINDOW_LENGTH-(to & WINDOW_MASK); + + spin_lock(&vmax301_spin); + vmax301_page(map, to); + memcpy_toio(map->map_priv_2 + to, from, thislen); + spin_unlock(&vmax301_spin); + to += thislen; + from += thislen; + len -= thislen; + } +} + +static struct map_info vmax_map[2] = { + { + "VMAX301 Internal Flash", + 3*2*1024*1024, + 1, + vmax301_read8, + vmax301_read16, + vmax301_read32, + vmax301_copy_from, + vmax301_write8, + vmax301_write16, + vmax301_write32, + vmax301_copy_to, + WINDOW_START + WINDOW_LENGTH, + 0xFFFFFFFF + }, + { + "VMAX301 Socket", + 0, + 1, + vmax301_read8, + vmax301_read16, + vmax301_read32, + vmax301_copy_from, + vmax301_write8, + vmax301_write16, + vmax301_write32, + vmax301_copy_to, + WINDOW_START + (3*WINDOW_LENGTH), + 0xFFFFFFFF + } +}; + +static struct mtd_info *vmax_mtd[2] = {NULL, NULL}; + +#if LINUX_VERSION_CODE < 0x20300 +#ifdef MODULE +#define init_vmax301 init_module +#define cleanup_vmax301 cleanup_module +#endif +#define __exit +#endif + +static void __exit cleanup_vmax301(void) +{ + int i; + + for (i=0; i<2; i++) { + if (vmax_mtd[i]) { + del_mtd_device(vmax_mtd[i]); + map_destroy(vmax_mtd[i]); + } + } + iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); +} + +int __init init_vmax301(void) +{ + int i; + unsigned long iomapadr; + // Print out our little header.. + printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START, + WINDOW_START+4*WINDOW_LENGTH); + + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4); + if (!iomapadr) { + printk("Failed to ioremap memory region\n"); + return -EIO; + } + /* Put the address in the map's private data area. + We store the actual MTD IO address rather than the + address of the first half, because it's used more + often. + */ + vmax_map[0].map_priv_1 = iomapadr + WINDOW_START; + vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START); + + for (i=0; i<2; i++) { + vmax_mtd[i] = do_cfi_probe(&vmax_map[i]); + if (!vmax_mtd[i]) + vmax_mtd[i] = do_jedec_probe(&vmax_map[i]); + if (!vmax_mtd[i]) + vmax_mtd[i] = do_ram_probe(&vmax_map[i]); + if (!vmax_mtd[i]) + vmax_mtd[i] = do_rom_probe(&vmax_map[i]); + if (vmax_mtd[i]) { + vmax_mtd[i]->module = THIS_MODULE; + add_mtd_device(vmax_mtd[i]); + } + } + + if (!vmax_mtd[1] && !vmax_mtd[2]) + return -ENXIO; + + return 0; +} + +#if LINUX_VERSION_CODE > 0x20300 +module_init(init_vmax301); +module_exit(cleanup_vmax301); +#endif diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 315f99e5b..25257b052 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -111,7 +111,6 @@ extern int de600_probe(struct net_device *); extern int de620_probe(struct net_device *); /* FDDI adapters */ -extern int dfx_probe(void); extern int apfddi_init(struct net_device *dev); extern int skfp_probe(struct net_device *dev); @@ -177,9 +176,6 @@ struct devprobe eisa_probes[] __initdata = { #ifdef CONFIG_NE3210 {ne3210_probe, 0}, #endif -#ifdef CONFIG_DEFXX - {dfx_probe, 0}, -#endif {NULL, 0}, }; diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index e5a60310c..681379708 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -1239,6 +1239,7 @@ int __init ltpc_probe(struct net_device *dev) return 0; } +#ifndef MODULE /* handles "ltpc=io,irq,dma" kernel command lines */ static int __init ltpc_setup(char *str) { @@ -1270,6 +1271,12 @@ static int __init ltpc_setup(char *str) } __setup("ltpc=", ltpc_setup); +#endif /* MODULE */ + +MODULE_PARM(debug, "i"); +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(dma, "i"); #ifdef MODULE @@ -1279,12 +1286,7 @@ static struct net_device dev_ltpc = { 0x0, 0, 0, 0, 0, NULL, ltpc_probe }; -MODULE_PARM(debug, "i"); -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(dma, "i"); - -int init_module(void) +int __init init_module(void) { int err, result; @@ -1306,8 +1308,9 @@ int init_module(void) return 0; } } +#endif -void cleanup_module(void) +static void __exit ltpc_cleanup(void) { long timeout; @@ -1360,5 +1363,5 @@ void cleanup_module(void) if(debug&DEBUG_VERBOSE) printk("returning from cleanup_module\n"); } -#endif /* MODULE */ +module_exit(ltpc_cleanup); diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 39939746d..096dd03c5 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -125,7 +125,7 @@ static unsigned int net_debug = NET_DEBUG; #include <linux/timer.h> static void atp_timed_checker(unsigned long ignored); static struct net_device *atp_timed_dev; -static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker}; +static struct timer_list atp_timer = { function: atp_timed_checker }; #endif /* Index to functions, as function prototypes. */ diff --git a/drivers/net/bonding.c b/drivers/net/bonding.c index 67f076d46..c806f028d 100644 --- a/drivers/net/bonding.c +++ b/drivers/net/bonding.c @@ -126,8 +126,6 @@ static void bond_set_multicast_list(struct net_device *master) slave->dev->flags = master->flags; slave->dev->set_multicast_list(slave->dev); } - - return 0; } static int bond_enslave(struct net_device *master, struct net_device *dev) @@ -182,7 +180,7 @@ static int bond_release(struct net_device *master, struct net_device *dev) } } - return 0; + return; } /* It is pretty silly, SIOCSIFHWADDR exists to make this. */ diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 841b314ee..d6f22eb04 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1558,7 +1558,10 @@ init_module(void) #if DEBUGGING net_debug = debug; +#else + debug = 0; #endif + dev_cs89x0.irq = irq; dev_cs89x0.base_addr = io; diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c index 1a8fedb5d..64f49180f 100644 --- a/drivers/net/defxx.c +++ b/drivers/net/defxx.c @@ -195,16 +195,12 @@ * device open. Updated transmit path to post a * single fragment which includes PRH->end of data. * Mar 2000 AC Did various cleanups for 2.3.x + * Jun 2000 jgarzik PCI and resource alloc cleanups */ -/* Version information string - should be updated prior to each new release!!! */ - -static const char *version = "defxx.c:v1.05 2000/03/26 Lawrence V. Stefani (stefani@lkg.dec.com) and others\n"; - /* Include files */ #include <linux/module.h> - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/string.h> @@ -226,6 +222,10 @@ static const char *version = "defxx.c:v1.05 2000/03/26 Lawrence V. Stefani (ste #include "defxx.h" +/* Version information string - should be updated prior to each new release!!! */ +static char version[] __initdata = + "defxx.c:v1.05a 2000/06/11 Lawrence V. Stefani (stefani@lkg.dec.com) and others\n"; + #define DYNAMIC_BUFFERS 1 #define SKBUFF_RX_COPYBREAK 200 @@ -235,10 +235,6 @@ static const char *version = "defxx.c:v1.05 2000/03/26 Lawrence V. Stefani (ste */ #define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX+128) -/* Define global routines */ - -int dfx_probe(void); - /* Define module-wide (static) routines */ static struct net_device *dfx_alloc_device(u16 iobase); @@ -428,22 +424,92 @@ static inline void dfx_port_read_long( */ static struct net_device *bp_root; +static int version_disp; +static int have_pci_driver; + +static struct pci_device_id dfx_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_FDDI, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dfx_pci_tbl); -int __init dfx_probe(void) +static int __init dfx_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + DFX_board_t *bp; /* board pointer */ + int port; + + if (!version_disp) { /* display version info if adapter is found */ + version_disp = 1; /* set display flag to TRUE so that */ + printk (version); /* we only display this string ONCE */ + } + + if (pci_enable_device (pdev)) + goto err_out; + pci_set_master (pdev); + + /* Get I/O base address from PCI Configuration Space */ + port = pci_resource_start (pdev, 1); + + if (!request_region (port, PFI_K_CSR_IO_LEN, "defxx")) { + printk (KERN_ERR + "defxx: I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", + port, (port + PFI_K_CSR_IO_LEN - 1)); + goto err_out; + } + + /* Allocate a new device structure for this adapter */ + dev = dfx_alloc_device (port); + if (dev == NULL) { + printk (KERN_ERR "defxx: alloc device failed\n"); + goto err_out_region; + } + + /* Initialize board structure with bus-specific info */ + bp = (DFX_board_t *) dev->priv; + bp->dev = dev; + bp->next = bp_root; + bp_root = dev; + bp->bus_type = DFX_BUS_TYPE_PCI; + bp->pci_dev = pdev; + + /* + * FIXME FIXME FIXME + * Suck! The original driver didn't clean up after + * itself at this stage, so we won't either. Someone + * needs to go back and see what (if anything) we need + * to free here... -jgarzik + */ + if (dfx_driver_init (dev) != DFX_K_SUCCESS) { + dev->base_addr = 0; /* clear port address field in device structure on failure */ + goto err_out_region; + } + + return 0; + +err_out_region: + release_region (port, PFI_K_CSR_IO_LEN); +err_out: + return -ENODEV; +}; + +static struct pci_driver dfx_driver = { + name: "defxx", + id_table: dfx_pci_tbl, + probe: dfx_init_one, +}; + +static int __init dfx_probe(void) { int i; /* used in for loops */ - int version_disp; /* was version info string already displayed? */ - int port_len; /* length of port address range (in bytes) */ u16 port; /* temporary I/O (port) address */ - struct pci_dev * pdev = NULL; /* PCI device record */ - u16 command; /* PCI Configuration space Command register val */ u32 slot_id; /* EISA hardware (slot) ID read from adapter */ DFX_board_t *bp; /* board pointer */ struct net_device *dev; DBG_printk("In dfx_probe...\n"); - version_disp = 0; /* default to version string not displayed */ already_probed = 1; /* set global flag */ /* Scan for FDDI EISA controllers */ @@ -463,9 +529,7 @@ int __init dfx_probe(void) port = (i << 12); /* recalc base addr */ /* Verify port address range is not already being used */ - - port_len = PI_ESIC_K_CSR_IO_LEN; - if (check_region(port, port_len) == 0) + if (request_region(port, PI_ESIC_K_CSR_IO_LEN, "defxx")) { /* Allocate a new device structure for this adapter */ @@ -485,70 +549,17 @@ int __init dfx_probe(void) } } else - printk("I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", port, (port + port_len-1)); + printk("I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", + port, (port + PI_ESIC_K_CSR_IO_LEN-1)); } } /* Scan for FDDI PCI controllers */ - - if (pci_present()) /* is PCI even present? */ - while ((pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_FDDI, pdev))) - { - if (!version_disp) /* display version info if adapter is found */ - { - version_disp = 1; /* set display flag to TRUE so that */ - printk(version); /* we only display this string ONCE */ - } - - if (pci_enable_device(pdev)) - continue; - - /* Verify that I/O enable bit is set (PCI slot is enabled) */ - - pci_read_config_word(pdev, PCI_COMMAND, &command); - if ((command & PCI_COMMAND_IO) == 0) - printk(KERN_ERR "defxx: I/O enable bit not set! Verify that slot is enabled\n"); - else - { - /* Turn off memory mapped space and enable mastering */ - - command |= PCI_COMMAND_MASTER; - command &= ~PCI_COMMAND_MEMORY; - pci_write_config_word(pdev, PCI_COMMAND, command); - - /* Get I/O base address from PCI Configuration Space */ - - port = pci_resource_start (pdev, 1); - - /* Verify port address range is not already being used */ - - port_len = PFI_K_CSR_IO_LEN; - - if (check_region(port, port_len) == 0) - { - /* Allocate a new device structure for this adapter */ - - dev = dfx_alloc_device(port); - if (dev != NULL) - { - /* Initialize board structure with bus-specific info */ - - bp = (DFX_board_t *) dev->priv; - bp->dev = dev; - bp->next = bp_root; - bp_root = dev; - bp->bus_type = DFX_BUS_TYPE_PCI; - bp->pci_dev = pdev; - if (dfx_driver_init(dev) == DFX_K_SUCCESS) - num_boards++; /* only increment global board count on success */ - else - dev->base_addr = 0; /* clear port address field in device structure on failure */ - } - } - else - printk(KERN_ERR "defxx: I/O range allocated to adapter (0x%X-0x%X) is already being used!\n", port, (port + port_len-1)); - } - } + i = pci_register_driver (&dfx_driver); + if (i > 0) { + num_boards += i; + have_pci_driver = 1; + } /* * If we're at this point we're going through dfx_probe() for the first @@ -556,10 +567,7 @@ int __init dfx_probe(void) * Otherwise, return failure (-ENODEV). */ - if (num_boards > 0) - return(0); - else - return(-ENODEV); + return (num_boards > 0) ? 0 : -ENODEV; } @@ -3464,28 +3472,34 @@ void dfx_xmt_flush( } -#ifdef MODULE - -int init_module(void) -{ - if(dfx_probe()<0) - return -ENODEV; - return 0; -} -void cleanup_module(void) +static void __exit dfx_cleanup(void) { while(bp_root!=NULL) { struct net_device *tmp=bp_root; DFX_board_t *priv=tmp->priv; bp_root=priv->next; + + /* FIXME: need to unregister FDDI device here? + * The original driver didn't do it, but I think so.. + * -jgarzik + */ + + if (priv->bus_type == DFX_BUS_TYPE_EISA) + release_region(tmp->base_addr, PI_ESIC_K_CSR_IO_LEN); + else + release_region(tmp->base_addr, PFI_K_CSR_IO_LEN); + kfree(tmp->priv); kfree(tmp); } + if (have_pci_driver) + pci_unregister_driver(&dfx_driver); } -#endif +module_init(dfx_probe); +module_exit(dfx_cleanup); /* diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c index 20865a48d..8ff607a01 100644 --- a/drivers/net/hamradio/baycom_par.c +++ b/drivers/net/hamradio/baycom_par.c @@ -63,6 +63,7 @@ * 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts * 0.8 12.02.2000 adapted to softnet driver interface * removed direct parport access, uses parport driver methods + * 0.9 03.07.2000 fix interface name handling */ /*****************************************************************************/ @@ -101,7 +102,7 @@ static const char bc_drvname[] = "baycom_par"; static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_par: version 0.8 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "baycom_par: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -512,14 +513,15 @@ static int __init init_baycompar(void) */ for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = baycom_device+i; - sprintf(dev->name, "bcp%d", i); + char ifname[IFNAMSIZ]; + sprintf(ifname, "bcp%d", i); if (!mode[i]) set_hw = 0; if (!set_hw) iobase[i] = 0; j = hdlcdrv_register_hdlcdrv(dev, &par96_ops, sizeof(struct baycom_state), - dev->name, iobase[i], 0, 0); + ifname, iobase[i], 0, 0); if (!j) { bc = (struct baycom_state *)dev->priv; if (set_hw && baycom_setmode(bc, mode[i])) diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index 6a6603ba3..cb43d038e 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -66,6 +66,7 @@ * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall * 0.8 10.08.1999 use module_init/module_exit * 0.9 12.02.2000 adapted to softnet driver interface + * 0.10 03.07.2000 fix interface name handling */ /*****************************************************************************/ @@ -88,7 +89,7 @@ static const char bc_drvname[] = "baycom_ser_fdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_ser_fdx: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "baycom_ser_fdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -628,14 +629,15 @@ static int __init init_baycomserfdx(void) */ for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = baycom_device+i; - sprintf(dev->name, "bcsf%d", i); + char ifname[IFNAMSIZ]; + sprintf(ifname, "bcsf%d", i); if (!mode[i]) set_hw = 0; if (!set_hw) iobase[i] = irq[i] = 0; j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops, sizeof(struct baycom_state), - dev->name, iobase[i], irq[i], 0); + ifname, iobase[i], irq[i], 0); if (!j) { bc = (struct baycom_state *)dev->priv; if (set_hw && baycom_setmode(bc, mode[i])) diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index 80483a171..dfe2312f0 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -56,6 +56,7 @@ * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall * 0.8 10.08.1999 use module_init/module_exit * 0.9 12.02.2000 adapted to softnet driver interface + * 0.10 03.07.2000 fix interface name handling */ /*****************************************************************************/ @@ -78,7 +79,7 @@ static const char bc_drvname[] = "baycom_ser_hdx"; static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "baycom_ser_hdx: version 0.9 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "baycom_ser_hdx: version 0.10 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -668,14 +669,15 @@ static int __init init_baycomserhdx(void) */ for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = baycom_device+i; - sprintf(dev->name, "bcsh%d", i); + char ifname[IFNAMSIZ]; + sprintf(ifname, "bcsh%d", i); if (!mode[i]) set_hw = 0; if (!set_hw) iobase[i] = irq[i] = 0; j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops, sizeof(struct baycom_state), - dev->name, iobase[i], irq[i], 0); + ifname, iobase[i], irq[i], 0); if (!j) { bc = (struct baycom_state *)dev->priv; if (set_hw && baycom_setmode(bc, mode[i])) diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c index c9bf6b119..c5a52b8ab 100644 --- a/drivers/net/hamradio/soundmodem/sm.c +++ b/drivers/net/hamradio/soundmodem/sm.c @@ -46,6 +46,7 @@ * removed some pre-2.2 kernel compatibility cruft * 0.10 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts * 0.11 12.02.2000 adapted to softnet driver interface + * 0.12 03.07.2000 fix interface name handling */ /*****************************************************************************/ @@ -65,7 +66,7 @@ /*static*/ const char sm_drvname[] = "soundmodem"; static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "soundmodem: version 0.11 compiled " __TIME__ " " __DATE__ "\n"; +KERN_INFO "soundmodem: version 0.12 compiled " __TIME__ " " __DATE__ "\n"; /* --------------------------------------------------------------------- */ @@ -648,8 +649,9 @@ static int __init init_soundmodem(void) */ for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = sm_device+i; - sprintf(dev->name, "sm%d", i); + char ifname[IFNAMSIZ]; + sprintf(ifname, "sm%d", i); if (!mode[i]) set_hw = 0; else { @@ -671,7 +673,7 @@ static int __init init_soundmodem(void) } if (!set_hw) iobase[i] = irq[i] = 0; - j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), dev->name, iobase[i], irq[i], dma[i]); + j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), ifname, iobase[i], irq[i], dma[i]); if (!j) { sm = (struct sm_state *)dev->priv; sm->hdrv.ptt_out.dma2 = dma2[i]; diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index df9c63621..57d57d339 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1989,7 +1989,6 @@ static void nsc_ircc_suspend(struct nsc_ircc_cb *self) static void nsc_ircc_wakeup(struct nsc_ircc_cb *self) { - struct net_device *dev = self->netdev; int iobase; if (!self->io.suspended) diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c index 06ebc072c..470d6dd4d 100644 --- a/drivers/net/irda/smc-ircc.c +++ b/drivers/net/irda/smc-ircc.c @@ -83,7 +83,9 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev); static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs); static void ircc_change_speed(void *priv, __u32 speed); static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +#if 0 /* unused */ static int ircc_is_receiving(struct ircc_cb *self); +#endif /* unused */ static int ircc_net_open(struct net_device *dev); static int ircc_net_close(struct net_device *dev); @@ -789,7 +791,6 @@ static int ircc_dma_receive(struct ircc_cb *self, int iobase) */ static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase) { - unsigned long flags; struct sk_buff *skb; int len, msgcnt; @@ -893,6 +894,7 @@ static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_unlock(&self->lock); } +#if 0 /* unused */ /* * Function ircc_is_receiving (self) * @@ -915,6 +917,7 @@ static int ircc_is_receiving(struct ircc_cb *self) return status; } +#endif /* unused */ /* * Function ircc_net_open (dev) @@ -990,8 +993,6 @@ static int ircc_net_close(struct net_device *dev) static void ircc_suspend(struct ircc_cb *self) { - int i = 10; - MESSAGE("%s, Suspending\n", driver_name); if (self->io.suspended) @@ -1004,7 +1005,6 @@ static void ircc_suspend(struct ircc_cb *self) static void ircc_wakeup(struct ircc_cb *self) { - struct net_device *dev = self->netdev; unsigned long flags; if (!self->io.suspended) diff --git a/drivers/net/ncr885e.c b/drivers/net/ncr885e.c index 9a5c65cb4..2500cb282 100644 --- a/drivers/net/ncr885e.c +++ b/drivers/net/ncr885e.c @@ -1144,18 +1144,11 @@ static int __init ncr885e_probe1(unsigned long ioaddr, unsigned char irq ) unsigned char *p; int i; - dev = init_etherdev(NULL, 0 ); - - /* construct private data for the 885 ethernet */ - dev->priv = kmalloc( sizeof( struct ncr885e_private ), GFP_KERNEL ); - - if ( dev->priv == NULL ) { - release_region( ioaddr, NCR885E_TOTAL_SIZE ); + dev = init_etherdev( NULL, sizeof( struct ncr885e_private ) ); + if (!dev) return -ENOMEM; - } - sp = (struct ncr885e_private *) dev->priv; - memset( sp, 0, sizeof( struct ncr885e_private )); + sp = dev->priv; /* snag the station address and display it */ for( i = 0; i < 3; i++ ) { @@ -1210,7 +1203,8 @@ static int __init ncr885e_probe(void) unsigned short cmd; unsigned char irq, latency; - while(( pdev = pci_find_device( PCI_VENDOR_ID_NCR, + /* use 'if' not 'while' where because driver only supports one device */ + if (( pdev = pci_find_device( PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C885_ETHERNET, pdev )) != NULL ) { @@ -1219,47 +1213,27 @@ static int __init ncr885e_probe(void) printk( KERN_INFO "%s", version ); } + if (pci_enable_device(pdev)) + continue; + /* Use I/O space */ - pci_read_config_dword( pdev, PCI_BASE_ADDRESS_0, &ioaddr ); - pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq ); + ioaddr = pci_resource_start (pdev, 0); + irq = pdev->irq; - ioaddr &= ~3; /* Adjust around the Grackle... */ #ifdef CONFIG_GEMINI ioaddr |= 0xfe000000; #endif - if ( check_region( ioaddr, NCR885E_TOTAL_SIZE )) + if ( !request_region( ioaddr, NCR885E_TOTAL_SIZE, "ncr885e" )) continue; /* finish off the probe */ if ( !(ncr885e_probe1(ioaddr, irq ))) { - chips++; - - /* Access is via I/O space, bus master enabled... */ - pci_read_config_word( pdev, PCI_COMMAND, &cmd ); - - if ( !(cmd & PCI_COMMAND_MASTER) ) { - printk( KERN_INFO " PCI master bit not set! Now setting.\n"); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_word( pdev, PCI_COMMAND, cmd ); - } - - if ( !(cmd & PCI_COMMAND_IO) ) { - printk( KERN_INFO " Enabling I/O space.\n" ); - cmd |= PCI_COMMAND_IO; - pci_write_config_word( pdev, PCI_COMMAND, cmd ); - } - - pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &latency ); - - if ( latency < 10 ) { - printk( KERN_INFO " PCI latency timer (CFLT) is unreasonably" - " low at %d. Setting to 255.\n", latency ); - pci_write_config_byte( pdev, PCI_LATENCY_TIMER, 255 ); - } - } + pci_set_master (pdev); + } else + release_region( ioaddr, NCR885E_TOTAL_SIZE ); } if ( !chips ) @@ -1401,14 +1375,10 @@ init_module(void) static void __exit cleanup_module(void) { - struct ncr885e_private *np; - if ( root_dev ) { - unregister_netdev( root_dev ); - np = (struct ncr885e_private *) root_dev->priv; release_region( root_dev->base_addr, NCR885E_TOTAL_SIZE ); - kfree( root_dev->priv ); + kfree( root_dev ); root_dev = NULL; } } diff --git a/drivers/net/pcmcia/aironet4500_cs.c b/drivers/net/pcmcia/aironet4500_cs.c index 1ff446fc2..d623828b9 100644 --- a/drivers/net/pcmcia/aironet4500_cs.c +++ b/drivers/net/pcmcia/aironet4500_cs.c @@ -602,7 +602,7 @@ static int awc_event(event_t event, int priority, -static int aironet_cs_init(void) +static int __init aironet_cs_init(void) { servinfo_t serv; @@ -619,7 +619,7 @@ static int aironet_cs_init(void) return 0; } -static void aironet_cs_exit(void) +static void __exit aironet_cs_exit(void) { DEBUG(0, "awc_cs: unloading %c ",'\n'); unregister_pcmcia_driver(&dev_info); @@ -635,5 +635,5 @@ static void aironet_cs_exit(void) } module_init(aironet_cs_init); -module_exit(aironet_cs_init); +module_exit(aironet_cs_exit); diff --git a/drivers/net/sk98lin/skvpd.c b/drivers/net/sk98lin/skvpd.c index 51751cbdc..de25856bc 100644 --- a/drivers/net/sk98lin/skvpd.c +++ b/drivers/net/sk98lin/skvpd.c @@ -233,6 +233,7 @@ int addr) /* VPD address */ 2: error, data verify error */ +#if 0 /* unused */ static int VpdWriteDWord( SK_AC *pAC, /* pAC pointer */ SK_IOC IoC, /* IO Context */ @@ -264,6 +265,7 @@ SK_U32 data) /* VPD data to write */ } return(0) ; } +#endif /* unused */ /* * Read one Stream of 'len' bytes of VPD data, starting at 'addr' from diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c index 0f0d15518..1a6da6e79 100644 --- a/drivers/net/sk_g16.c +++ b/drivers/net/sk_g16.c @@ -615,7 +615,6 @@ int __init SK_init(struct net_device *dev) } /* End of SK_init */ -static int io = 0; /* 0 == probe */ MODULE_AUTHOR("Patrick J.D. Weichmann"); MODULE_DESCRIPTION("Schneider & Koch G16 Ethernet Device Driver"); MODULE_PARM(io, "i"); @@ -623,6 +622,8 @@ MODULE_PARM_DESC(io, "0 to probe common ports (unsafe), or the I/O base of the b #ifdef MODULE +static int io = 0; /* 0 == probe */ + static int __init SK_init_module (void) { int rc; diff --git a/drivers/net/skfp/ecm.c b/drivers/net/skfp/ecm.c index 411169286..9dcca7f55 100644 --- a/drivers/net/skfp/ecm.c +++ b/drivers/net/skfp/ecm.c @@ -432,8 +432,8 @@ int cmd ; static void prop_actions(smc) struct s_smc *smc ; { - int port_in ; - int port_out ; + int port_in = 0 ; + int port_out = 0 ; RS_SET(smc,RS_EVENT) ; switch (smc->s.sas) { diff --git a/drivers/net/skfp/ess.c b/drivers/net/skfp/ess.c index 868237b30..a4c6409b4 100644 --- a/drivers/net/skfp/ess.c +++ b/drivers/net/skfp/ess.c @@ -64,8 +64,8 @@ static const u_short plist_raf_alc_res[] = { SMT_P0012, SMT_P320B, SMT_P320F, static const u_short plist_raf_chg_req[] = { SMT_P320B, SMT_P320F, SMT_P3210, SMT_P001A, 0 } ; -static const struct fddi_addr smt_sba_da = {0x80,0x01,0x43,0x00,0x80,0x0C} ; -static const struct fddi_addr null_addr = {0,0,0,0,0,0} ; +static const struct fddi_addr smt_sba_da = {{0x80,0x01,0x43,0x00,0x80,0x0C}} ; +static const struct fddi_addr null_addr = {{0,0,0,0,0,0}} ; /* ------------------------------------------------------------- diff --git a/drivers/net/skfp/fplustm.c b/drivers/net/skfp/fplustm.c index f5bda2464..4d816c584 100644 --- a/drivers/net/skfp/fplustm.c +++ b/drivers/net/skfp/fplustm.c @@ -69,9 +69,9 @@ static char cam_warning [] = "E_SMT_004: CAM still busy\n"; } \ } -const struct fddi_addr fddi_broadcast = {0xff,0xff,0xff,0xff,0xff,0xff}; -static const struct fddi_addr null_addr = {0,0,0,0,0,0} ; -static const struct fddi_addr dbeacon_multi = {0x01,0x80,0xc2,0x00,0x01,0x00}; +const struct fddi_addr fddi_broadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +static const struct fddi_addr null_addr = {{0,0,0,0,0,0}}; +static const struct fddi_addr dbeacon_multi = {{0x01,0x80,0xc2,0x00,0x01,0x00}}; static const u_short my_said = 0xffff ; /* short address (n.u.) */ static const u_short my_sagp = 0xffff ; /* short group address (n.u.) */ diff --git a/drivers/net/skfp/smt.c b/drivers/net/skfp/smt.c index ed8538958..b02ab9a2d 100644 --- a/drivers/net/skfp/smt.c +++ b/drivers/net/skfp/smt.c @@ -51,7 +51,7 @@ static const char *const smt_class_name[] = { #define LAST_CLASS (SMT_PMF_SET) static const struct fddi_addr SMT_Unknown = { - 0,0,0x1f,0,0,0 + { 0,0,0x1f,0,0,0 } } ; /* diff --git a/drivers/net/skfp/srf.c b/drivers/net/skfp/srf.c index 79bbfedc6..d5f091c54 100644 --- a/drivers/net/skfp/srf.c +++ b/drivers/net/skfp/srf.c @@ -391,7 +391,7 @@ struct s_smc *smc ; int i ; static const struct fddi_addr SMT_SRF_DA = { - 0x80, 0x01, 0x43, 0x00, 0x80, 0x08 + { 0x80, 0x01, 0x43, 0x00, 0x80, 0x08 } } ; /* diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index 86c04c5c7..084fd4120 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -1591,9 +1591,8 @@ static void smc_set_multicast_list(struct net_device *dev) #ifdef MODULE -static char devicename[9] = { 0, }; static struct net_device devSMC9194 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ + "", /* device name is inserted by linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0, 0, /* I/O address, IRQ */ 0, 0, 0, NULL, smc_init }; diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index ae14cf96d..5db78eb16 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1287,7 +1287,6 @@ static void lance_set_multicast(struct net_device *dev) static void lance_set_multicast_retry(unsigned long _opaque) { struct net_device *dev = (struct net_device *) _opaque; - struct lance_private *lp = (struct lance_private *) dev->priv; lance_set_multicast(dev); } diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 971bea2cf..b59414255 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -295,7 +295,9 @@ static int __init ibmtr_probe1(struct net_device *dev, int PIOaddr) struct tok_info *ti=0; __u32 cd_chanid; unsigned char *tchanid, ctemp; +#ifndef PCMCIA unsigned long timeout; +#endif #ifndef MODULE #ifndef PCMCIA diff --git a/drivers/net/wan/comx-proto-lapb.c b/drivers/net/wan/comx-proto-lapb.c index abf8977ff..0e2737ac3 100644 --- a/drivers/net/wan/comx-proto-lapb.c +++ b/drivers/net/wan/comx-proto-lapb.c @@ -518,11 +518,7 @@ static struct comx_protocol comx25_protocol = { NULL }; -#ifdef MODULE -#define comx_proto_lapb_init init_module -#endif - -__initfunc(int comx_proto_lapb_init(void)) +int __init comx_proto_lapb_init(void) { int ret; @@ -532,11 +528,13 @@ __initfunc(int comx_proto_lapb_init(void)) return comx_register_protocol(&comx25_protocol); } -#ifdef MODULE -void cleanup_module(void) +static void __exit comx_proto_lapb_exit(void) { comx_unregister_protocol(comxlapb_protocol.name); comx_unregister_protocol(comx25_protocol.name); } -#endif /* MODULE */ +#ifdef MODULE +module_init(comx_proto_lapb_init); +#endif +module_exit(comx_proto_lapb_exit); diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index c27d89ca9..dc0b212f5 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -395,7 +395,7 @@ static int __init cosa_init(void) cosa_cards[i].num = -1; for (i=0; io[i] != 0 && i < MAX_CARDS; i++) cosa_probe(io[i], irq[i], dma[i]); - devfs_handle = devfs_mk_dir (NULL, "cosa", 4, NULL); + devfs_handle = devfs_mk_dir (NULL, "cosa", NULL); devfs_register_series (devfs_handle, "%u", nr_cards, DEVFS_FL_DEFAULT, cosa_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index a4d038f05..b28fdafc1 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -222,19 +222,17 @@ static struct sv11_device *sv11_init(int iobase, int irq) { struct z8530_dev *dev; struct sv11_device *sv; - int i; unsigned long flags; /* * Get the needed I/O space */ - if(check_region(iobase, 8)) + if(!request_region(iobase, 8, "Comtrol SV11")) { printk(KERN_WARNING "hostess: I/O 0x%X already in use.\n", iobase); return NULL; } - request_region(iobase, 8, "Comtrol SV11"); sv=(struct sv11_device *)kmalloc(sizeof(struct sv11_device), GFP_KERNEL); if(!sv) diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 274e5f286..c0b17f79f 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -1043,7 +1043,6 @@ int lmc_probe (struct net_device *dev) /*fold00*/ { int pci_index = 0; unsigned long pci_ioaddr; - unsigned short pci_command; unsigned int pci_irq_line; u16 vendor, subvendor, device, subdevice; u32 foundaddr = 0; diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index 1dd9a4ae2..f22c69b2a 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,30 @@ +2000-06-30 Petr Vandrovec <vandrove@vc.cvut.cz> + + * procfs.c (do_hardware_modes): Generated string can be up to 34 + chars long. + +2000-06-20 Gunther Mayer <gunther.mayer@braunschweig.okersurf.de> + + * parport_pc.c (parport_pc_compat_write_block_pio): Warn about + change_mode failures. + (parport_pc_ecp_write_block_pio): Likewise. + (parport_pc_ecp_read_block_pio): Likewise. + +2000-06-20 Gunther Mayer <gunther.mayer@braunschweig.okersurf.de> + + * parport_pc.c (parport_SPP_supported): Warn more about possibly + incorrect parameters. + +2000-06-15 Tim Waugh <twaugh@redhat.com> + + * parport_pc.c (parport_ECP_supported): Set PARPORT_MODE_COMPAT + for ECP ports, since they can all do hardware accelerated + compatibility mode (I assume). + +2000-06-13 Tim Waugh <twaugh@redhat.com> + + * parport_pc.c (cleanup_module): Remark about possible bugs. + 2000-06-13 Tim Waugh <twaugh@redhat.com> * procfs.c: Break 'hardware' out into separate files. diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 13a4faea4..12b56f585 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -714,7 +714,9 @@ size_t parport_pc_compat_write_block_pio (struct parport *port, /* Set up parallel port FIFO mode.*/ parport_pc_data_forward (port); /* Must be in PS2 mode */ parport_pc_frob_control (port, PARPORT_CONTROL_STROBE, 0); - change_mode (port, ECR_PPF); /* Parallel port FIFO */ + r = change_mode (port, ECR_PPF); /* Parallel port FIFO */ + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_PPF failed\n", port->name); + port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; /* Write the data to the FIFO. */ @@ -793,7 +795,8 @@ size_t parport_pc_ecp_write_block_pio (struct parport *port, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD, 0); - change_mode (port, ECR_ECP); /* ECP FIFO */ + r = change_mode (port, ECR_ECP); /* ECP FIFO */ + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name); port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA; /* Write the data to the FIFO. */ @@ -917,7 +920,8 @@ size_t parport_pc_ecp_read_block_pio (struct parport *port, PARPORT_CONTROL_STROBE | PARPORT_CONTROL_AUTOFD, 0); - change_mode (port, ECR_ECP); /* ECP FIFO */ + r = change_mode (port, ECR_ECP); /* ECP FIFO */ + if (r) printk (KERN_DEBUG "%s: Warning change_mode ECR_ECP failed\n", port->name); port->ieee1284.phase = IEEE1284_PH_REV_DATA; /* Do the transfer. */ @@ -1412,6 +1416,12 @@ static int __devinit get_superio_irq (struct parport *p) /* * Checks for port existence, all ports support SPP MODE + * Returns: + * 0 : No parallel port at this adress + * PARPORT_MODE_PCSPP : SPP port detected + * (if the user specified an ioport himself, + * this shall always be the case!) + * */ static int __devinit parport_SPP_supported(struct parport *pb) { @@ -1447,8 +1457,8 @@ static int __devinit parport_SPP_supported(struct parport *pb) if (user_specified) /* That didn't work, but the user thinks there's a * port here. */ - printk (KERN_DEBUG "0x%lx: CTR: wrote 0x%02x, read 0x%02x\n", - pb->base, w, r); + printk (KERN_DEBUG "parport 0x%lx (WARNING): CTR: " + "wrote 0x%02x, read 0x%02x\n", pb->base, w, r); /* Try the data register. The data lines aren't tri-stated at * this stage, so we expect back what we wrote. */ @@ -1463,11 +1473,15 @@ static int __devinit parport_SPP_supported(struct parport *pb) return PARPORT_MODE_PCSPP; } - if (user_specified) + if (user_specified) { /* Didn't work, but the user is convinced this is the * place. */ - printk (KERN_DEBUG "0x%lx: DATA: wrote 0x%02x, read 0x%02x\n", - pb->base, w, r); + printk (KERN_DEBUG "parport 0x%lx (WARNING): DATA: " + "wrote 0x%02x, read 0x%02x\n", pb->base, w, r); + printk (KERN_DEBUG "parport 0x%lx: You gave this address, " + "but there is probably no parallel port there!\n", + pb->base); + } /* It's possible that we can't read the control register or * the data register. In that case just believe the user. */ @@ -1692,7 +1706,7 @@ static int __devinit parport_ECP_supported(struct parport *pb) /* Go back to mode 000 */ frob_econtrol (pb, 0xe0, ECR_SPP << 5); - pb->modes |= PARPORT_MODE_ECP; + pb->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; return 1; } @@ -2598,6 +2612,7 @@ int init_module(void) void cleanup_module(void) { + /* We ought to keep track of which ports are actually ours. */ struct parport *p = parport_enumerate(), *tmp; if (!user_specified) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 15efdd657..b82b4e2f2 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -198,7 +198,7 @@ static int do_hardware_modes (ctl_table *table, int write, size_t *lenp) { struct parport *port = (struct parport *)table->extra1; - char buffer[20]; + char buffer[40]; int len = 0; if (filp->f_pos) { diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 0c42771ee..f278c9e41 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -309,50 +309,63 @@ static void reset_socket(u_long i); static void unreset_socket(u_long i); static void parse_events(void *info, u_int events); -int register_ss_entry(int nsock, struct pccard_operations * ss_entry) +socket_info_t *pcmcia_register_socket (int slot, + struct pccard_operations * ss_entry, + int use_bus_pm) { - int i, ns; socket_info_t *s; + int i; - DEBUG(0, "cs: register_ss_entry(%d, 0x%p)\n", nsock, ss_entry); + DEBUG(0, "cs: pcmcia_register_socket(0x%p)\n", ss_entry); + + s = kmalloc(sizeof(struct socket_info_t), GFP_KERNEL); + memset(s, 0, sizeof(socket_info_t)); + + s->ss_entry = ss_entry; + s->sock = slot; + s->setup.data = sockets; + s->setup.function = &setup_socket; + s->shutdown.data = sockets; + s->shutdown.function = &shutdown_socket; + /* base address = 0, map = 0 */ + s->cis_mem.flags = 0; + s->cis_mem.speed = cis_speed; + s->use_bus_pm = use_bus_pm; + s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; + spin_lock_init(&s->lock); + + for (i = 0; i < sockets; i++) + if (socket_table[i] == NULL) break; + socket_table[i] = s; + if (i == sockets) sockets++; - for (ns = 0; ns < nsock; ns++) { - s = kmalloc(sizeof(struct socket_info_t), GFP_KERNEL); - memset(s, 0, sizeof(socket_info_t)); - - s->ss_entry = ss_entry; - s->sock = ns; - s->setup.data = sockets; - s->setup.function = &setup_socket; - s->shutdown.data = sockets; - s->shutdown.function = &shutdown_socket; - /* base address = 0, map = 0 */ - s->cis_mem.flags = 0; - s->cis_mem.speed = cis_speed; - s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; - spin_lock_init(&s->lock); - - for (i = 0; i < sockets; i++) - if (socket_table[i] == NULL) break; - socket_table[i] = s; - if (i == sockets) sockets++; - - init_socket(s); - ss_entry->inquire_socket(ns, &s->cap); + init_socket(s); + ss_entry->inquire_socket(slot, &s->cap); #ifdef CONFIG_PROC_FS - if (proc_pccard) { - char name[3]; - sprintf(name, "%02d", i); - s->proc = proc_mkdir(name, proc_pccard); - if (s->proc) - ss_entry->proc_setup(ns, s->proc); + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", i); + s->proc = proc_mkdir(name, proc_pccard); + if (s->proc) + ss_entry->proc_setup(slot, s->proc); #ifdef PCMCIA_DEBUG - if (s->proc) - create_proc_read_entry("clients", 0, s->proc, - proc_read_clients, s); + if (s->proc) + create_proc_read_entry("clients", 0, s->proc, + proc_read_clients, s); #endif - } + } #endif + return s; +} /* pcmcia_register_socket */ + +int register_ss_entry(int nsock, struct pccard_operations * ss_entry) +{ + int ns; + + DEBUG(0, "cs: register_ss_entry(%d, 0x%p)\n", nsock, ss_entry); + + for (ns = 0; ns < nsock; ns++) { + pcmcia_register_socket (ns, ss_entry, 0); } return 0; @@ -360,49 +373,57 @@ int register_ss_entry(int nsock, struct pccard_operations * ss_entry) /*====================================================================*/ -void unregister_ss_entry(struct pccard_operations * ss_entry) +void pcmcia_unregister_socket(socket_info_t *s) { - int i, j; - socket_info_t *s = NULL; + int j, socket = -1; client_t *client; + for (j = 0; j < MAX_SOCK; j++) + if (socket_table [j] == s) { + socket = j; + break; + } + if (socket < 0) + return; + #ifdef CONFIG_PROC_FS - for (i = 0; i < sockets; i++) { - s = socket_table[i]; - if (s->ss_entry != ss_entry) continue; - if (proc_pccard) { - char name[3]; - sprintf(name, "%02d", i); + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", socket); #ifdef PCMCIA_DEBUG - remove_proc_entry("clients", s->proc); + remove_proc_entry("clients", s->proc); #endif - remove_proc_entry(name, proc_pccard); - } + remove_proc_entry(name, proc_pccard); } #endif - for (;;) { - for (i = 0; i < sockets; i++) { - s = socket_table[i]; - if (s->ss_entry == ss_entry) break; - } - if (i == sockets) - break; - shutdown_socket(i); - release_cis_mem(s); - while (s->clients) { - client = s->clients; - s->clients = s->clients->next; - kfree(client); - } - s->ss_entry = NULL; - kfree(s); - socket_table[i] = NULL; - for (j = i; j < sockets-1; j++) - socket_table[j] = socket_table[j+1]; - sockets--; + shutdown_socket(socket); + release_cis_mem(s); + while (s->clients) { + client = s->clients; + s->clients = s->clients->next; + kfree(client); + } + s->ss_entry = NULL; + kfree(s); + + socket_table[socket] = NULL; + for (j = socket; j < sockets-1; j++) + socket_table[j] = socket_table[j+1]; + sockets--; +} /* pcmcia_unregister_socket */ + +void unregister_ss_entry(struct pccard_operations * ss_entry) +{ + int i; + + for (i = 0; i < sockets; i++) { + socket_info_t *socket = socket_table[i]; + if (socket->ss_entry == ss_entry) + pcmcia_unregister_socket (socket); + else + i++; } - } /* unregister_ss_entry */ /*====================================================================== @@ -675,35 +696,51 @@ static void parse_events(void *info, u_int events) ======================================================================*/ +void pcmcia_suspend_socket (socket_info_t *s) +{ + if ((s->state & SOCKET_PRESENT) && !(s->state & SOCKET_SUSPEND)) { + send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); + suspend_socket(s); + s->state |= SOCKET_SUSPEND; + } +} + +void pcmcia_resume_socket (socket_info_t *s) +{ + int stat; + + /* Do this just to reinitialize the socket */ + init_socket(s); + get_socket_status(s, &stat); + + /* If there was or is a card here, we need to do something + about it... but parse_events will sort it all out. */ + if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT)) + parse_events(s, SS_DETECT); +} + static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data) { - int i, stat; + int i; socket_info_t *s; - + + /* only for busses that don't suspend/resume slots directly */ + switch (rqst) { case PM_SUSPEND: DEBUG(1, "cs: received suspend notification\n"); for (i = 0; i < sockets; i++) { - s = socket_table[i]; - if ((s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_SUSPEND)){ - send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); - suspend_socket(s); - s->state |= SOCKET_SUSPEND; - } + s = socket_table [i]; + if (!s->use_bus_pm) + pcmcia_suspend_socket (socket_table [i]); } break; case PM_RESUME: DEBUG(1, "cs: received resume notification\n"); for (i = 0; i < sockets; i++) { - s = socket_table[i]; - /* Do this just to reinitialize the socket */ - init_socket(s); - get_socket_status(s, &stat); - /* If there was or is a card here, we need to do something - about it... but parse_events will sort it all out. */ - if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT)) - parse_events(s, SS_DETECT); + s = socket_table [i]; + if (!s->use_bus_pm) + pcmcia_resume_socket (socket_table [i]); } break; } @@ -2331,6 +2368,11 @@ EXPORT_SYMBOL(MTDHelperEntry); EXPORT_SYMBOL(proc_pccard); #endif +EXPORT_SYMBOL(pcmcia_register_socket); +EXPORT_SYMBOL(pcmcia_unregister_socket); +EXPORT_SYMBOL(pcmcia_suspend_socket); +EXPORT_SYMBOL(pcmcia_resume_socket); + static int __init init_pcmcia_cs(void) { printk(KERN_INFO "%s\n", release); diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index f355c337b..abd518a7f 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -160,6 +160,7 @@ typedef struct socket_info_t { #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc; #endif + int use_bus_pm; } socket_info_t; /* Flags in config state */ diff --git a/drivers/pcmcia/pci_socket.c b/drivers/pcmcia/pci_socket.c index 3f4463e75..9cc901603 100644 --- a/drivers/pcmcia/pci_socket.c +++ b/drivers/pcmcia/pci_socket.c @@ -28,6 +28,14 @@ #include "pci_socket.h" + +extern struct socket_info_t *pcmcia_register_socket (int slot, + struct pccard_operations *vtable, int use_bus_pm); +extern void pcmcia_unregister_socket (struct socket_info_t *socket); +extern void pcmcia_suspend_socket (struct socket_info_t *socket); +extern void pcmcia_resume_socket (struct socket_info_t *socket); + + /* * Arbitrary define. This is the array of active cardbus * entries. @@ -161,45 +169,87 @@ static struct pccard_operations pci_socket_operations = { pci_proc_setup }; -static int __init add_pci_socket(int nr, struct pci_dev *dev, struct pci_socket_ops *ops) +static int __devinit add_pci_socket(int nr, struct pci_dev *dev, struct pci_socket_ops *ops) { pci_socket_t *socket = nr + pci_socket_array; memset(socket, 0, sizeof(*socket)); socket->dev = dev; socket->op = ops; + dev->driver_data = socket; init_waitqueue_head(&socket->wait); return socket->op->open(socket); } -static int __init pci_socket_init(void) +static int __devinit +cardbus_probe (struct pci_dev *dev, const struct pci_device_id *id) { - struct pci_dev *dev = NULL; - int nr = 0; - - while ((dev = pci_find_class(PCI_CLASS_BRIDGE_CARDBUS << 8, dev)) != NULL) { - printk("Adding cardbus controller %d: %s\n", nr, dev->name); - add_pci_socket(nr, dev, ¥ta_operations); - nr++; + int s; + + for (s = 0; s < MAX_SOCKETS; s++) { + if (pci_socket_array [s].dev == 0) { + add_pci_socket (s, dev, ¥ta_operations); + pci_socket_array [s].pcmcia_socket = + pcmcia_register_socket (s, + &pci_socket_operations, + 1); + return 0; + } } + return -1; +} - if (nr <= 0) - return -1; - register_ss_entry(nr, &pci_socket_operations); - return 0; +static void __devexit cardbus_remove (struct pci_dev *dev) +{ + pci_socket_t *socket = (pci_socket_t *) dev->driver_data; + + pcmcia_unregister_socket (socket->pcmcia_socket); + if (socket->op && socket->op->close) + socket->op->close(socket); + dev->driver_data = 0; +} + +static void cardbus_suspend (struct pci_dev *dev) +{ + pci_socket_t *socket = (pci_socket_t *) dev->driver_data; + pcmcia_suspend_socket (socket->pcmcia_socket); } -static void __exit pci_socket_exit(void) +static void cardbus_resume (struct pci_dev *dev) { - int i; + pci_socket_t *socket = (pci_socket_t *) dev->driver_data; + pcmcia_resume_socket (socket->pcmcia_socket); +} - unregister_ss_entry(&pci_socket_operations); - for (i = 0; i < MAX_SOCKETS; i++) { - pci_socket_t *socket = pci_socket_array + i; - if (socket->op && socket->op->close) - socket->op->close(socket); - } +static struct pci_device_id cardbus_table [] = { { + class: PCI_CLASS_BRIDGE_CARDBUS << 8, + class_mask: ~0, + + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, +}, { /* all zeroes */ } +}; + +static struct pci_driver pci_cardbus_driver = { + name: "cardbus", + id_table: cardbus_table, + probe: cardbus_probe, + remove: cardbus_remove, + suspend: cardbus_suspend, + resume: cardbus_resume, +}; + +static int __devinit pci_socket_init(void) +{ + return pci_module_init (&pci_cardbus_driver); +} + +static void __devexit pci_socket_exit (void) +{ + pci_unregister_driver (&pci_cardbus_driver); } module_init(pci_socket_init); diff --git a/drivers/pcmcia/pci_socket.h b/drivers/pcmcia/pci_socket.h index 8db7c68b5..6893fb1c2 100644 --- a/drivers/pcmcia/pci_socket.h +++ b/drivers/pcmcia/pci_socket.h @@ -8,6 +8,7 @@ #define __PCI_SOCKET_H struct pci_socket_ops; +struct socket_info_t; typedef struct pci_socket { struct pci_dev *dev; @@ -19,6 +20,7 @@ typedef struct pci_socket { socket_cap_t cap; wait_queue_head_t wait; unsigned int events; + struct socket_info_t *pcmcia_socket; /* A few words of private data for the low-level driver.. */ unsigned int private[8]; diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index 586945b10..a4db8b9c4 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -2175,7 +2175,7 @@ int unregister_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex) /* Unregister ourselves with devfs */ for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) { sparcaudio_mkname (name_buf, dev_list[i].name, drv->index); - de = devfs_find_handle (devfs_handle, name_buf, 0, 0, 0, + de = devfs_find_handle (devfs_handle, name_buf, 0, 0, DEVFS_SPECIAL_CHR, 0); devfs_unregister (de); } @@ -2219,7 +2219,7 @@ int __init sparcaudio_init(void) if (devfs_register_chrdev(SOUND_MAJOR, "sparcaudio", &sparcaudio_fops)) return -EIO; - devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "sound", NULL); #ifdef CONFIG_SPARCAUDIO_AMD7930 amd7930_init(); diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index 6dfc4c132..ebe9928c2 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -1029,7 +1029,7 @@ int __init bpp_init(void) instances[idx].opened = 0; probeLptPort(idx); } - devfs_handle = devfs_mk_dir (NULL, "bpp", 3, NULL); + devfs_handle = devfs_mk_dir (NULL, "bpp", NULL); devfs_register_series (devfs_handle, "%u", BPP_NO, DEVFS_FL_DEFAULT, BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &bpp_fops, NULL); diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index 458fe20af..e197661f7 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -701,5 +701,6 @@ void cleanup_module(void) { misc_deregister(&jsf_dev); if (unregister_blkdev(JSFD_MAJOR, "jsfd") != 0) printk("jsfd: cleanup_module failed\n"); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); } #endif diff --git a/drivers/sbus/char/sab82532.c b/drivers/sbus/char/sab82532.c index 51fa5a68c..378148159 100644 --- a/drivers/sbus/char/sab82532.c +++ b/drivers/sbus/char/sab82532.c @@ -2391,9 +2391,6 @@ void cleanup_module(void) /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ save_flags(flags); cli(); - timer_active &= ~(1 << RS_TIMER); - timer_table[RS_TIMER].fn = NULL; - timer_table[RS_TIMER].expires = 0; remove_bh(SERIAL_BH); if ((e1 = tty_unregister_driver(&serial_driver))) printk("SERIAL: failed to unregister serial driver (%d)\n", diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 589b787d0..eed298c6e 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -672,7 +672,7 @@ static int vfc_probe(void) kfree(vfc_dev_lst); return -EIO; } - devfs_handle = devfs_mk_dir (NULL, "vfc", 3, NULL); + devfs_handle = devfs_mk_dir (NULL, "vfc", NULL); instance = 0; for_all_sbusdev(sdev, sbus) { diff --git a/drivers/sbus/char/zs.c b/drivers/sbus/char/zs.c index fb687102c..f9512e12c 100644 --- a/drivers/sbus/char/zs.c +++ b/drivers/sbus/char/zs.c @@ -913,14 +913,6 @@ static int startup(struct sun_serial * info) info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* - * Set up serial timers... - */ -#if 0 /* Works well and stops the machine. */ - timer_table[RS_TIMER].expires = jiffies + 2; - timer_active |= 1 << RS_TIMER; -#endif - - /* * and set the speed of the serial port */ change_speed(info); @@ -2404,8 +2396,6 @@ int __init zs_init(void) /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = zs_timer; - timer_table[RS_TIMER].expires = 0; show_serial_version(); diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index 24861d034..13d7dc398 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -1057,7 +1057,6 @@ static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout) oldto = scp->timeout_per_command; scp->timeout_per_command = timeout; -#if LINUX_VERSION_CODE >= 0x02014B if (timeout == 0) { del_timer(&scp->eh_timeout); scp->eh_timeout.data = (unsigned long) NULL; @@ -1069,17 +1068,5 @@ static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout) scp->eh_timeout.expires = jiffies + timeout; add_timer(&scp->eh_timeout); } -#else - if (timeout > 0) { - if (timer_table[SCSI_TIMER].expires == 0) { - timer_table[SCSI_TIMER].expires = jiffies + timeout; - timer_active |= 1 << SCSI_TIMER; - } else { - if (jiffies + timeout < timer_table[SCSI_TIMER].expires) - timer_table[SCSI_TIMER].expires = jiffies + timeout; - } - } -#endif - return oldto; } diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index b7d3b9cc1..51a4eabc6 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1529,7 +1529,7 @@ int __init scsi_dev_init(void) /* Yes we're here... */ - scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL); /* * This makes /proc/scsi and /proc/scsi/scsi visible. */ @@ -2646,7 +2646,7 @@ int init_module(void) scsi_loadable_module_flag = 1; - scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", 4, NULL); + scsi_devfs_handle = devfs_mk_dir (NULL, "scsi", NULL); scsi_host_no_init (scsihosts); /* * This is where the processing takes place for most everything diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 042493e3e..362173b56 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -87,6 +87,7 @@ int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int at_head) SCpnt->request.cmd = SPECIAL; SCpnt->request.special = (void *) SCpnt; SCpnt->request.q = NULL; + SCpnt->request.free_list = NULL; SCpnt->request.nr_segments = 0; /* diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 152f8afc3..056b2f1b5 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -278,7 +278,6 @@ void scan_scsis(struct Scsi_Host *shpnt, * is pointless work. */ scsi_initialize_queue(SDpnt, shpnt); - blk_queue_headactive(&SDpnt->request_queue, 0); SDpnt->request_queue.queuedata = (void *) SDpnt; /* Make sure we have something that is valid for DMA purposes */ scsi_result = ((!shpnt->unchecked_isa_dma) @@ -425,8 +424,10 @@ void scan_scsis(struct Scsi_Host *shpnt, } /* Last device block does not exist. Free memory. */ - if (SDpnt != NULL) + if (SDpnt != NULL) { + blk_cleanup_queue(&SDpnt->request_queue); kfree((char *) SDpnt); + } /* If we allocated a buffer so we could do DMA, free it now */ if (scsi_result != &scsi_result0[0] && scsi_result != NULL) { @@ -594,7 +595,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, sprintf (devname, "host%d/bus%d/target%d/lun%d", SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname); - else SDpnt->de = devfs_mk_dir (scsi_devfs_handle, devname, 0, NULL); + else SDpnt->de = devfs_mk_dir (scsi_devfs_handle, devname, NULL); for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) @@ -691,8 +692,6 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun, * is pointless work. */ scsi_initialize_queue(SDpnt, shpnt); - blk_queue_headactive(&SDpnt->request_queue, 0); - SDpnt->request_queue.queuedata = (void *) SDpnt; SDpnt->host = shpnt; initialize_merge_fn(SDpnt); diff --git a/drivers/sgi/char/sgiserial.c b/drivers/sgi/char/sgiserial.c index 8b3126340..b0ccff4f8 100644 --- a/drivers/sgi/char/sgiserial.c +++ b/drivers/sgi/char/sgiserial.c @@ -665,19 +665,6 @@ static void do_serial_hangup(void *private_) } -/* - * This subroutine is called when the RS_TIMER goes off. It is used - * by the serial driver to handle ports that do not have an interrupt - * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530. - */ - -static void rs_timer(void) -{ - printk("rs_timer called\n"); - prom_halt(); - return; -} - static int startup(struct sgi_serial * info) { volatile unsigned char junk; @@ -751,14 +738,6 @@ static int startup(struct sgi_serial * info) info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* - * Set up serial timers... - */ -#if 0 /* Works well and stops the machine. */ - timer_table[RS_TIMER].expires = jiffies + 2; - timer_active |= 1 << RS_TIMER; -#endif - - /* * and set the speed of the serial port */ change_speed(info); @@ -1842,8 +1821,6 @@ int rs_init(void) /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; show_serial_version(); diff --git a/drivers/sound/cmpci.c b/drivers/sound/cmpci.c index 527d570cf..3b5639054 100644 --- a/drivers/sound/cmpci.c +++ b/drivers/sound/cmpci.c @@ -312,7 +312,6 @@ struct cm_state { /* --------------------------------------------------------------------- */ static struct cm_state *devs = NULL; -static struct cm_state *devaudio = NULL; static unsigned long wavetable_mem = 0; /* --------------------------------------------------------------------- */ @@ -2462,10 +2461,8 @@ int __init init_cmpci(void) err_irq: if(s->iosynth) release_region(s->iosynth, CM_EXTENT_SYNTH); - err_region1: if(s->iomidi) release_region(s->iomidi, CM_EXTENT_MIDI); - err_region4: release_region(s->iobase, CM_EXTENT_CODEC); err_region5: kfree_s(s, sizeof(struct cm_state)); diff --git a/drivers/sound/esssolo1.c b/drivers/sound/esssolo1.c index 930e58f46..cfc89d3ee 100644 --- a/drivers/sound/esssolo1.c +++ b/drivers/sound/esssolo1.c @@ -273,6 +273,7 @@ static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char d write_seq(s, data); } +#if 0 /* unused */ static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) { unsigned char r; @@ -282,6 +283,7 @@ static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg) read_seq(s, &r); return r; } +#endif /* unused */ static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data) { diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index ce5cd829e..58d5ef474 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -28,7 +28,6 @@ #include "sound_config.h" #include "sound_firmware.h" -#include "soundmodule.h" #include "mpu401.h" diff --git a/drivers/sound/sonicvibes.c b/drivers/sound/sonicvibes.c index 0d50924d8..c9c66a73f 100644 --- a/drivers/sound/sonicvibes.c +++ b/drivers/sound/sonicvibes.c @@ -535,7 +535,7 @@ static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate) { unsigned long flags; - unsigned char r, m, n; + unsigned char r, m=0, n=0; unsigned xm, xn, xr, xd, metric = ~0U; /* the warnings about m and n used uninitialized are bogus and may safely be ignored */ diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index 9ec821b91..938b77968 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -63,7 +63,7 @@ extern int msnd_classic_init(void); extern int msnd_pinnacle_init(void); #endif #ifdef CONFIG_SOUND_CMPCI -extern init_cmpci(void); +extern int init_cmpci(void); #endif /* @@ -559,7 +559,7 @@ int soundcore_init(void) printk(KERN_ERR "soundcore: sound device already in use.\n"); return -EBUSY; } - devfs_handle = devfs_mk_dir (NULL, "sound", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "sound", NULL); /* * Now init non OSS drivers */ diff --git a/drivers/sound/sound_syms.c b/drivers/sound/sound_syms.c index b6f2855be..a3601dcf6 100644 --- a/drivers/sound/sound_syms.c +++ b/drivers/sound/sound_syms.c @@ -53,7 +53,8 @@ extern int softoss_dev; EXPORT_SYMBOL(softoss_dev); /* Locking */ -#include "soundmodule.h" +extern struct notifier_block *sound_locker; +extern void sound_notifier_chain_register(struct notifier_block *); EXPORT_SYMBOL(sound_locker); EXPORT_SYMBOL(sound_notifier_chain_register); diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index aa7b82280..0351fe7ce 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -43,7 +43,7 @@ #include <linux/delay.h> #include <linux/proc_fs.h> #include <linux/smp_lock.h> -#include "soundmodule.h" +#include <linux/notifier.h> struct notifier_block *sound_locker=(struct notifier_block *)0; @@ -572,7 +572,7 @@ static void soundcard_register_devfs (int do_register) else { devfs_handle_t de; - de = devfs_find_handle (NULL, name_buf, 0, 0, 0, + de = devfs_find_handle (NULL, name_buf, 0, 0, DEVFS_SPECIAL_CHR, 0); devfs_unregister (de); } diff --git a/drivers/sound/sscape.c b/drivers/sound/sscape.c index 89470da82..b9ac2879e 100644 --- a/drivers/sound/sscape.c +++ b/drivers/sound/sscape.c @@ -272,12 +272,14 @@ static int host_read(struct sscape_info *devc) return data; } +#if 0 /* unused */ static int host_command1(struct sscape_info *devc, int cmd) { unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); return host_write(devc, buf, 1); } +#endif /* unused */ static int host_command2(struct sscape_info *devc, int cmd, int parm1) diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c index 8f4fd8e6d..7fa446c00 100644 --- a/drivers/tc/zs.c +++ b/drivers/tc/zs.c @@ -559,10 +559,6 @@ static void do_softint(void *private_) } } -static void rs_timer(void) -{ -} - static int startup(struct dec_serial * info) { unsigned long flags; @@ -1664,8 +1660,6 @@ int __init zs_init(void) /* Setup base handler, and timer table. */ init_bh(SERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; /* Find out how many Z8530 SCCs we have */ if (zs_chain == 0) diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index d6cedee9b..5609ae1c2 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -514,6 +514,7 @@ static int findintfif(struct usb_device *dev, unsigned int ifn) extern struct list_head usb_driver_list; +#if 0 static int finddriver(struct usb_driver **driver, char *name) { struct list_head *tmp; @@ -533,6 +534,7 @@ static int finddriver(struct usb_driver **driver, char *name) return -EINVAL; } +#endif /* * file operations @@ -715,6 +717,27 @@ static int proc_resetep(struct dev_state *ps, void *arg) return 0; } +static int proc_clearhalt(struct dev_state *ps, void *arg) +{ + unsigned int ep; + int pipe; + int ret; + + get_user_ret(ep, (unsigned int *)arg, -EFAULT); + if ((ret = findintfep(ps->dev, ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + if (ep & USB_DIR_IN) + pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f); + else + pipe = usb_sndbulkpipe(ps->dev, ep & 0x7f); + + usb_clear_halt(ps->dev, pipe); + return 0; +} + + static int proc_getdriver(struct dev_state *ps, void *arg) { struct usbdevfs_getdriver gd; @@ -1120,6 +1143,12 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd case USBDEVFS_RESET: ret = proc_resetdevice(ps); break; + + case USBDEVFS_CLEAR_HALT: + ret = proc_clearhalt(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; case USBDEVFS_GETDRIVER: ret = proc_getdriver(ps, (void *)arg); diff --git a/drivers/usb/ibmcam.c b/drivers/usb/ibmcam.c index f643dee47..1001362e6 100644 --- a/drivers/usb/ibmcam.c +++ b/drivers/usb/ibmcam.c @@ -2933,11 +2933,11 @@ static int ibmcam_find_struct(void) * 1/22/00 Moved camera init code to ibmcam_open() * 1/27/00 Changed to use static structures, added locking. * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 7/3/00 Fixed endianness bug. */ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) { struct usb_ibmcam *ibmcam = NULL; - const unsigned char *p_rev; const struct usb_interface_descriptor *interface; const struct usb_endpoint_descriptor *endpoint; int devnum, model=0; @@ -2955,20 +2955,24 @@ static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) return NULL; /* Check the version/revision */ - p_rev = (const unsigned char *) &dev->descriptor.bcdDevice; - if (p_rev[1] == 0x00 && p_rev[0] == 0x02) { + switch (dev->descriptor.bcdDevice) { + case 0x0002: if (ifnum != 2) return NULL; - printk(KERN_INFO "IBM USB camera found (model 1).\n"); + printk(KERN_INFO "IBM USB camera found (model 1, rev. 0x%04x).\n", + dev->descriptor.bcdDevice); model = IBMCAM_MODEL_1; - } else if (p_rev[1] == 0x03 && p_rev[0] == 0x0A) { + break; + case 0x030A: if (ifnum != 0) return NULL; - printk(KERN_INFO "IBM USB camera found (model 2).\n"); + printk(KERN_INFO "IBM USB camera found (model 2, rev. 0x%04x).\n", + dev->descriptor.bcdDevice); model = IBMCAM_MODEL_2; - } else { - printk(KERN_ERR "IBM camera revision=%02x.%02x not supported\n", - p_rev[1], p_rev[0]); + break; + default: + printk(KERN_ERR "IBM camera with revision 0x%04x is not supported.\n", + dev->descriptor.bcdDevice); return NULL; } diff --git a/drivers/usb/input.c b/drivers/usb/input.c index 4fb2985af..a21df12dc 100644 --- a/drivers/usb/input.c +++ b/drivers/usb/input.c @@ -401,7 +401,7 @@ static int __init input_init(void) printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); return -EBUSY; } - input_devfs_handle = devfs_mk_dir(NULL, "input", 5, NULL); + input_devfs_handle = devfs_mk_dir(NULL, "input", NULL); return 0; } diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 261d5ac00..12a2c5796 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (07/03/2000) gkh + * Added more debugging to serial_ioctl call + * * (06/25/2000) gkh * Changed generic_write_bulk_callback to not call wake_up_interruptible * directly, but to have port_softint do it at a safer time. @@ -644,7 +647,7 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in return -ENODEV; } - dbg(__FUNCTION__ " - port %d", port->number); + dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd); if (!port->active) { dbg (__FUNCTION__ " - port not open"); diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index fa7f475c6..a455e8dc8 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (07/03/2000) gkh + * Added visor_set_ioctl and visor_set_termios functions (they don't do much + * of anything, but are good for debugging.) + * * (06/25/2000) gkh * Fixed bug in visor_unthrottle that should help with the disconnect in PPP * bug that people have been reporting. @@ -59,6 +63,8 @@ static void visor_close (struct usb_serial_port *port, struct file *filp); static void visor_throttle (struct usb_serial_port *port); static void visor_unthrottle (struct usb_serial_port *port); static int visor_startup (struct usb_serial *serial); +static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); +static void visor_set_termios (struct usb_serial_port *port, struct termios *old_termios); /* All of the device info needed for the Handspring Visor */ static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID; @@ -79,6 +85,8 @@ struct usb_serial_device_type handspring_device = { throttle: visor_throttle, unthrottle: visor_unthrottle, startup: visor_startup, + ioctl: visor_ioctl, + set_termios: visor_set_termios, }; @@ -211,3 +219,78 @@ static int visor_startup (struct usb_serial *serial) return (0); } + +static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +{ + dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd); + + return -ENOIOCTLCMD; +} + + +/* This function is all nice and good, but we don't change anything based on it :) */ +static void visor_set_termios (struct usb_serial_port *port, struct termios *old_termios) +{ + unsigned int cflag = port->tty->termios->c_cflag; + + dbg(__FUNCTION__ " - port %d", port->number); + + /* check that they really want us to change something */ + if (old_termios) { + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { + dbg(__FUNCTION__ " - nothing to change..."); + return; + } + } + + if ((!port->tty) || (!port->tty->termios)) { + dbg(__FUNCTION__" - no tty structures"); + return; + } + + /* get the byte size */ + switch (cflag & CSIZE) { + case CS5: dbg(__FUNCTION__ " - data bits = 5"); break; + case CS6: dbg(__FUNCTION__ " - data bits = 6"); break; + case CS7: dbg(__FUNCTION__ " - data bits = 7"); break; + default: + case CS8: dbg(__FUNCTION__ " - data bits = 8"); break; + } + + /* determine the parity */ + if (cflag & PARENB) + if (cflag & PARODD) + dbg(__FUNCTION__ " - parity = odd"); + else + dbg(__FUNCTION__ " - parity = even"); + else + dbg(__FUNCTION__ " - parity = none"); + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + dbg(__FUNCTION__ " - stop bits = 2"); + else + dbg(__FUNCTION__ " - stop bits = 1"); + + + /* figure out the flow control settings */ + if (cflag & CRTSCTS) + dbg(__FUNCTION__ " - RTS/CTS is enabled"); + else + dbg(__FUNCTION__ " - RTS/CTS is disabled"); + + /* determine software flow control */ + if (I_IXOFF(port->tty)) + dbg(__FUNCTION__ " - XON/XOFF is enabled, XON = %2x, XOFF = %2x", START_CHAR(port->tty), STOP_CHAR(port->tty)); + else + dbg(__FUNCTION__ " - XON/XOFF is disabled"); + + /* get the baud rate wanted */ + dbg(__FUNCTION__ " - baud rate = %d", tty_get_baud_rate(port->tty)); + + return; +} + + + diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index c168efa02..91afddd56 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -11,6 +11,11 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (07/04/2000) gkh + * Added support for port settings. Baud rate can now be changed. Line signals + * are not transferred to and from the tty layer yet, but things seem to be + * working well now. + * * (05/04/2000) gkh * First cut at open and close commands. Data can flow through the ports at * default speeds now. @@ -55,6 +60,7 @@ /* function prototypes for the Connect Tech WhiteHEAT serial converter */ static int whiteheat_open (struct usb_serial_port *port, struct file *filp); static void whiteheat_close (struct usb_serial_port *port, struct file *filp); +static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old); static void whiteheat_throttle (struct usb_serial_port *port); static void whiteheat_unthrottle (struct usb_serial_port *port); @@ -93,6 +99,7 @@ struct usb_serial_device_type whiteheat_device = { close: whiteheat_close, throttle: whiteheat_throttle, unthrottle: whiteheat_unthrottle, + ioctl: whiteheat_ioctl, set_termios: whiteheat_set_termios, shutdown: whiteheat_shutdown, }; @@ -103,6 +110,13 @@ struct whiteheat_private { }; +/* local function prototypes */ +static inline void set_rts (struct usb_serial_port *port, unsigned char rts); +static inline void set_dtr (struct usb_serial_port *port, unsigned char dtr); +static inline void set_break (struct usb_serial_port *port, unsigned char brk); + + + #define COMMAND_PORT 4 #define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ @@ -116,7 +130,7 @@ static void command_port_write_callback (struct urb *urb) int i; #endif - dbg ("command_port_write_callback"); + dbg (__FUNCTION__); if (urb->status) { dbg ("nonzero urb status: %d", urb->status); @@ -125,7 +139,7 @@ static void command_port_write_callback (struct urb *urb) #ifdef DEBUG if (urb->actual_length) { - printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } @@ -145,16 +159,16 @@ static void command_port_read_callback (struct urb *urb) int i; #endif - dbg ("command_port_write_callback"); + dbg (__FUNCTION__); if (urb->status) { - dbg ("nonzero urb status: %d", urb->status); + dbg (__FUNCTION__ " - nonzero urb status: %d", urb->status); return; } #ifdef DEBUG if (urb->actual_length) { - printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } @@ -179,7 +193,7 @@ static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *da int timeout; __u8 *transfer_buffer; - dbg("whiteheat_send_cmd: %d", command); + dbg(__FUNCTION__" - command %d", command); port = &serial->port[COMMAND_PORT]; info = (struct whiteheat_private *)port->private; @@ -189,8 +203,10 @@ static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *da transfer_buffer[0] = command; memcpy (&transfer_buffer[1], data, datasize); port->write_urb->transfer_buffer_length = datasize + 1; - if (usb_submit_urb (port->write_urb)) - dbg ("submit urb failed"); + if (usb_submit_urb (port->write_urb)) { + dbg (__FUNCTION__" - submit urb failed"); + return -1; + } /* wait for the command to complete */ timeout = COMMAND_TIMEOUT; @@ -199,6 +215,7 @@ static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *da } if (info->command_finished == FALSE) { + dbg (__FUNCTION__ " - command timed out."); return -1; } @@ -212,10 +229,10 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) struct usb_serial_port *command_port; struct whiteheat_private *info; - dbg("whiteheat_open port %d", port->number); + dbg(__FUNCTION__" - port %d", port->number); if (port->active) { - dbg ("device already open"); + dbg (__FUNCTION__ " - device already open"); return -EINVAL; } port->active = 1; @@ -225,7 +242,7 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) if (command_port->private == NULL) { info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); if (info == NULL) { - err("out of memory"); + err(__FUNCTION__ " - out of memory"); return -ENOMEM; } @@ -238,7 +255,7 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) /* Start reading from the device */ if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); /* send an open port command */ open_command.port = port->number - port->minor; @@ -247,9 +264,9 @@ static int whiteheat_open (struct usb_serial_port *port, struct file *filp) /* Need to do device specific setup here (control lines, baud rate, etc.) */ /* FIXME!!! */ - dbg("whiteheat_open exit"); + dbg(__FUNCTION__ " - exit"); - return (0); + return 0; } @@ -257,7 +274,7 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) { struct whiteheat_min_set close_command; - dbg("whiteheat_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); /* send a close command to the port */ close_command.port = port->number - port->minor; @@ -273,30 +290,102 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) } +static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +{ + dbg(__FUNCTION__ " - port %d, cmd 0x%.4x", port->number, cmd); + + return -ENOIOCTLCMD; +} + + static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios) { unsigned int cflag = port->tty->termios->c_cflag; + struct whiteheat_port_settings port_settings; - dbg("whiteheat_set_termios port %d", port->number); + dbg(__FUNCTION__ " -port %d", port->number); /* check that they really want us to change something */ if (old_termios) { if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("nothing to change..."); + dbg(__FUNCTION__ " - nothing to change..."); return; } } - /* do the parsing of the cflag to see what to set the line to */ - /* FIXME!! */ + if ((!port->tty) || (!port->tty->termios)) { + dbg(__FUNCTION__" - no tty structures"); + return; + } + + /* get the byte size */ + switch (cflag & CSIZE) { + case CS5: port_settings.bits = 5; break; + case CS6: port_settings.bits = 6; break; + case CS7: port_settings.bits = 7; break; + default: + case CS8: port_settings.bits = 8; break; + } + dbg(__FUNCTION__ " - data bits = %d", port_settings.bits); + + /* determine the parity */ + if (cflag & PARENB) + if (cflag & PARODD) + port_settings.parity = 'o'; + else + port_settings.parity = 'e'; + else + port_settings.parity = 'n'; + dbg(__FUNCTION__ " - parity = %c", port_settings.parity); + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + port_settings.stop = 2; + else + port_settings.stop = 1; + dbg(__FUNCTION__ " - stop bits = %d", port_settings.stop); + + + /* figure out the flow control settings */ + if (cflag & CRTSCTS) + port_settings.hflow = (WHITEHEAT_CTS_FLOW | WHITEHEAT_RTS_FLOW); + else + port_settings.hflow = 0; + dbg(__FUNCTION__ " - hardware flow control = %s %s %s %s", + (port_settings.hflow | WHITEHEAT_CTS_FLOW) ? "CTS" : "", + (port_settings.hflow | WHITEHEAT_RTS_FLOW) ? "RTS" : "", + (port_settings.hflow | WHITEHEAT_DSR_FLOW) ? "DSR" : "", + (port_settings.hflow | WHITEHEAT_DTR_FLOW) ? "DTR" : ""); + + /* determine software flow control */ + if (I_IXOFF(port->tty)) + port_settings.sflow = 'b'; + else + port_settings.sflow = 'n'; + dbg(__FUNCTION__ " - software flow control = %c", port_settings.sflow); + + port_settings.xon = START_CHAR(port->tty); + port_settings.xoff = STOP_CHAR(port->tty); + dbg(__FUNCTION__ " - XON = %2x, XOFF = %2x", port_settings.xon, port_settings.xoff); + + /* get the baud rate wanted */ + port_settings.baud = tty_get_baud_rate(port->tty); + dbg(__FUNCTION__ " - baud rate = %d", port_settings.baud); + /* handle any settings that aren't specified in the tty structure */ + port_settings.lloop = 0; + + /* now send the message to the device */ + whiteheat_send_cmd (port->serial, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); + return; } + static void whiteheat_throttle (struct usb_serial_port *port) { - dbg("whiteheat_throttle port %d", port->number); + dbg(__FUNCTION__" - port %d", port->number); /* Change the control signals */ /* FIXME!!! */ @@ -307,7 +396,7 @@ static void whiteheat_throttle (struct usb_serial_port *port) static void whiteheat_unthrottle (struct usb_serial_port *port) { - dbg("whiteheat_unthrottle port %d", port->number); + dbg(__FUNCTION__" - port %d", port->number); /* Change the control signals */ /* FIXME!!! */ @@ -334,7 +423,7 @@ static int whiteheat_startup (struct usb_serial *serial) int response; const struct whiteheat_hex_record *record; - dbg("whiteheat_startup"); + dbg(__FUNCTION__); response = ezusb_set_reset (serial, 1); @@ -343,7 +432,7 @@ static int whiteheat_startup (struct usb_serial *serial) response = ezusb_writememory (serial, record->address, (unsigned char *)record->data, record->data_size, 0xa0); if (response < 0) { - err("ezusb_writememory failed for loader (%d %04X %p %d)", + err(__FUNCTION__ " - ezusb_writememory failed for loader (%d %04X %p %d)", response, record->address, record->data, record->data_size); break; } @@ -360,7 +449,7 @@ static int whiteheat_startup (struct usb_serial *serial) response = ezusb_writememory (serial, record->address, (unsigned char *)record->data, record->data_size, 0xa3); if (response < 0) { - err("ezusb_writememory failed for first firmware step (%d %04X %p %d)", + err(__FUNCTION__ " - ezusb_writememory failed for first firmware step (%d %04X %p %d)", response, record->address, record->data, record->data_size); break; } @@ -374,7 +463,7 @@ static int whiteheat_startup (struct usb_serial *serial) response = ezusb_writememory (serial, record->address, (unsigned char *)record->data, record->data_size, 0xa0); if (response < 0) { - err("ezusb_writememory failed for second firmware step (%d %04X %p %d)", + err(__FUNCTION__" - ezusb_writememory failed for second firmware step (%d %04X %p %d)", response, record->address, record->data, record->data_size); break; } @@ -392,9 +481,9 @@ static void whiteheat_shutdown (struct usb_serial *serial) { struct usb_serial_port *command_port; - dbg("whiteheat_shutdown"); + dbg(__FUNCTION__); - /* set up some stuff for our command port */ + /* free up our private data for our command port */ command_port = &serial->port[COMMAND_PORT]; if (command_port->private != NULL) { kfree (command_port->private); @@ -404,3 +493,35 @@ static void whiteheat_shutdown (struct usb_serial *serial) return; } + + + +static void set_command (struct usb_serial_port *port, unsigned char state, unsigned char command) +{ + struct whiteheat_rdb_set rdb_command; + + /* send a set rts command to the port */ + rdb_command.port = port->number - port->minor; + rdb_command.state = state; + + whiteheat_send_cmd (port->serial, command, (__u8 *)&rdb_command, sizeof(rdb_command)); +} + + +static inline void set_rts (struct usb_serial_port *port, unsigned char rts) +{ + set_command (port, rts, WHITEHEAT_SET_RTS); +} + + +static inline void set_dtr (struct usb_serial_port *port, unsigned char dtr) +{ + set_command (port, dtr, WHITEHEAT_SET_DTR); +} + + +static inline void set_break (struct usb_serial_port *port, unsigned char brk) +{ + set_command (port, brk, WHITEHEAT_SET_BREAK); +} + diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index b1d2d2a01..d1155e0ee 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -307,7 +307,7 @@ static void ohci_dump_roothub (ohci_t *controller, int verbose) static void ohci_dump (ohci_t *controller, int verbose) { - dbg ("OHCI controller %p state", controller->regs); + dbg ("OHCI controller %s state", controller->ohci_dev->slot_name); // dumps some of the state we know about ohci_dump_status (controller); @@ -831,8 +831,8 @@ static int ep_unlink (ohci_t * ohci, ed_t * ed) } break; - case INT: - int_branch = ed->int_branch; + case INT: + int_branch = ed->int_branch; interval = ed->int_interval; for (i = 0; i < ep_rev (6, interval); i += inter) { @@ -1392,6 +1392,14 @@ static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) __u8 data[8]; num_ports = readl (&ohci->regs->roothub.a) & RH_A_NDP; + if (num_ports > MAX_ROOT_PORTS) { + err ("bogus NDP=%d for OHCI %s", num_ports, + ohci->ohci_dev->slot_name); + err ("rereads as NDP=%d", + readl (&ohci->regs->roothub.a) & RH_A_NDP); + /* retry later; "should not happen" */ + return 0; + } *(__u8 *) data = (readl (&ohci->regs->roothub.status) & (RH_HS_LPSC | RH_HS_OCIC)) ? 1: 0; ret = *(__u8 *) data; @@ -1424,8 +1432,12 @@ static void rh_int_timer_do (unsigned long ptr) ohci_t * ohci = urb->dev->bus->hcpriv; if (ohci->disabled) - return; - + return; + + /* ignore timers firing during PM suspend, etc */ + if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) + return; + if(ohci->rh.send) { len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length); if (len > 0) { @@ -1702,7 +1714,9 @@ static int hc_reset (ohci_t * ohci) /* Disable HC interrupts */ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); - dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control)); + dbg("USB HC reset_hc %s: ctrl = %x ;", + ohci->ohci_dev->slot_name, + readl (&ohci->regs->control)); /* Reset USB (needed by some controllers) */ writel (0, &ohci->regs->control); @@ -1793,17 +1807,24 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) { ints = OHCI_INTR_WDH; - } else { - if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) - return; + } else if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) { + return; } // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no)); if (ints & OHCI_INTR_UE) { ohci->disabled++; - err ("OHCI Unrecoverable Error, controller disabled"); + err ("OHCI Unrecoverable Error, controller %s disabled", + ohci->ohci_dev->slot_name); // e.g. due to PCI Master/Target Abort + +#ifndef DEBUG + // FIXME: be optimistic, hope that bug won't repeat often. + // Make some non-interrupt context restart the controller. + // Count and limit the retries though; either hardware or + // software errors can go forever... +#endif } if (ints & OHCI_INTR_WDH) { @@ -1823,7 +1844,8 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) if (ohci->ed_rm_list[!frame] != NULL) { dl_del_list (ohci, !frame); } - if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); } writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); @@ -1885,20 +1907,20 @@ static ohci_t * hc_alloc_ohci (void * mem_base) static void hc_release_ohci (ohci_t * ohci) { - dbg("USB HC release ohci"); + dbg ("USB HC release ohci %s", ohci->ohci_dev->slot_name); /* disconnect all devices */ if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); - hc_reset (ohci); - writel (OHCI_USB_RESET, &ohci->regs->control); - wait_ms (10); + if (!ohci->disabled) + hc_reset (ohci); if (ohci->irq >= 0) { free_irq (ohci->irq, ohci); ohci->irq = -1; } + ohci->ohci_dev->driver_data = 0; usb_deregister_bus (ohci->bus); usb_free_bus (ohci->bus); @@ -1926,13 +1948,15 @@ static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) #endif printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", (unsigned long) mem_base, bufp); - printk(KERN_INFO __FILE__ ": %s\n", dev->name); + printk(KERN_INFO __FILE__ ": pci slot %s, %s\n", dev->slot_name, dev->name); ohci = hc_alloc_ohci (mem_base); if (!ohci) { return -ENOMEM; } + ohci->ohci_dev = dev; + dev->driver_data = ohci; INIT_LIST_HEAD (&ohci->ohci_hcd_list); list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); @@ -1960,7 +1984,6 @@ static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) #ifdef DEBUG ohci_dump (ohci, 1); #endif - return 0; } err("request interrupt %d failed", irq); @@ -2037,7 +2060,7 @@ static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) switch (rqst) { case PM_SUSPEND: /* act as if usb suspend can always be used */ - dbg("USB suspend: %p", ohci->regs); + dbg("USB suspend: %s", ohci->ohci_dev->slot_name); ohci->hc_control = OHCI_USB_SUSPEND; writel (ohci->hc_control, &ohci->regs->control); wait_ms (10); @@ -2050,7 +2073,7 @@ static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) switch (temp) { case OHCI_USB_RESET: // lost power - dbg("USB reset: %p", ohci->regs); + dbg("USB reset: %s", ohci->ohci_dev->slot_name); ohci->disabled = 1; if (ohci->bus->root_hub) usb_disconnect (&ohci->bus->root_hub); @@ -2058,14 +2081,16 @@ static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { ohci->disabled = 1; - err ("can't restart, %d", temp); + err ("can't restart %s, %d", + ohci->ohci_dev->slot_name, + temp); } dbg ("reset done"); break; case OHCI_USB_SUSPEND: // host wakeup case OHCI_USB_RESUME: // remote wakeup - dbg("USB resume: %p", ohci->regs); + dbg("USB resume: %s", ohci->ohci_dev->slot_name); ohci->hc_control = OHCI_USB_RESUME; writel (ohci->hc_control, &ohci->regs->control); wait_ms (20); diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h index 3ac5dafb2..e326f5ee6 100644 --- a/drivers/usb/usb-ohci.h +++ b/drivers/usb/usb-ohci.h @@ -8,13 +8,9 @@ */ -#define MODSTR "ohci: " - - static int cc_to_error[16] = { /* mapping of the OHCI CC status to error codes */ -#ifdef USB_ST_CRC /* status codes */ /* No Error */ USB_ST_NOERROR, /* CRC Error */ USB_ST_CRC, /* Bit Stuff */ USB_ST_BITSTUFF, @@ -33,28 +29,6 @@ static int cc_to_error[16] = { /* Not Access */ USB_ST_NORESPONSE }; -#else /* error codes */ - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* reservd */ -ETIMEDOUT, - /* reservd */ -ETIMEDOUT, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ECOMM, - /* Not Access */ -ETIMEDOUT, - /* Not Access */ -ETIMEDOUT -}; -#define USB_ST_URB_PENDING -EINPROGRESS -#endif - - struct ed; struct td; @@ -410,6 +384,7 @@ typedef struct ohci { struct usb_bus * bus; struct usb_device * dev[128]; struct virt_root_hub rh; + struct pci_dev *ohci_dev; } ohci_t; diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index ba9b44ac3..626bfc93c 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -1393,8 +1393,10 @@ _static int uhci_submit_iso_urb (urb_t *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; +#ifdef ISO_SANITY_CHECK int pipe=urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); +#endif int n, ret, last=0; uhci_desc_t *td, **tdm; int status, destination; @@ -1422,18 +1424,18 @@ _static int uhci_submit_iso_urb (urb_t *urb) tdm[n] = 0; continue; } - - if(urb->iso_frame_desc[n].length > maxsze) { + #ifdef ISO_SANITY_CHECK + if(urb->iso_frame_desc[n].length > maxsze) { err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); tdm[n] = 0; ret=-EINVAL; goto inval; -#endif } - + else +#endif ret = alloc_td (&td, UHCI_PTR_DEPTH); - inval: + if (ret) { int i; // Cleanup allocated TDs diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 6529b53c5..0b30ebb38 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -119,7 +119,7 @@ if [ "$CONFIG_FB" = "y" ]; then fi tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY tristate ' ATI Rage 128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 - bool ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX + tristate ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX tristate ' SIS 630/540 display support (EXPERIMENTAL)' CONFIG_FB_SIS fi fi @@ -240,7 +240,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \ "$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_P9100" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ - "$CONFIG_FB_RIVA" = "m" -o \ + "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_3DFX" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB8 m @@ -263,7 +263,7 @@ if [ "$CONFIG_FB" = "y" ]; then if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VESA" = "m" -o \ "$CONFIG_FB_VIRTUAL" = "m" -o "$CONFIG_FB_TBOX" = "m" -o \ - "$CONFIG_FB_Q40" = "m" -o \ + "$CONFIG_FB_Q40" = "m" -o "$CONFIG_FB_3DFX" = "m" -o \ "$CONFIG_FB_CONTROL" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \ "$CONFIG_FB_VALKYRIE" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ @@ -306,6 +306,7 @@ if [ "$CONFIG_FB" = "y" ]; then "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_PLATINUM" = "m" -o \ "$CONFIG_FB_MATROX" = "m" -o "$CONFIG_FB_PM2" = "m" -o \ "$CONFIG_FB_RIVA" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \ + "$CONFIG_FB_3DFX" = "m" -o \ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_SIS" = "m" ]; then define_tristate CONFIG_FBCON_CFB32 m fi diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c index 08fcf8141..d32a621b7 100644 --- a/drivers/video/atyfb.c +++ b/drivers/video/atyfb.c @@ -612,6 +612,7 @@ static inline void aty_st_8(unsigned int regindex, u8 val, writeb (val, info->ati_regbase + regindex); } +#if defined(CONFIG_PPC) || defined(CONFIG_PMAC_PBOOK) static void aty_st_lcd(int index, u32 val, const struct fb_info_aty *info) { unsigned long temp; @@ -633,6 +634,7 @@ static u32 aty_ld_lcd(int index, const struct fb_info_aty *info) /* read the register value */ return aty_ld_le32(LCD_DATA, info); } +#endif /* * Generic Mach64 routines diff --git a/drivers/video/clgenfb.c b/drivers/video/clgenfb.c index bb8d302fc..bdabd1e45 100644 --- a/drivers/video/clgenfb.c +++ b/drivers/video/clgenfb.c @@ -2557,7 +2557,6 @@ static int __init clgen_pci_setup (struct clgenfb_info *info, #endif /* CONFIG_FB_OF */ struct pci_dev *pdev; unsigned long board_addr, board_size; - u16 tmp16; DPRINTK ("ENTER\n"); @@ -2618,14 +2617,12 @@ static int __init clgen_pci_setup (struct clgenfb_info *info, } if (!request_mem_region(board_addr, board_size, "clgenfb")) { - pci_write_config_word (pdev, PCI_COMMAND, tmp16); printk(KERN_ERR "clgen: cannot reserve region 0x%lx, abort\n", board_addr); return -1; } #if 0 /* if the system didn't claim this region, we would... */ if (!request_mem_region(0xA0000, 65535, "clgenfb")) { - pci_write_config_word (pdev, PCI_COMMAND, tmp16); printk(KERN_ERR "clgen: cannot reserve region 0x%lx, abort\n", 0xA0000L); release_mem_region(board_addr, board_size); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3707b611a..2ab527f02 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -709,7 +709,7 @@ fbmem_init(void) create_proc_read_entry("fb", 0, 0, fbmem_read_proc, NULL); - devfs_handle = devfs_mk_dir (NULL, "fb", 0, NULL); + devfs_handle = devfs_mk_dir (NULL, "fb", NULL); if (devfs_register_chrdev(FB_MAJOR,"fb",&fb_fops)) printk("unable to get major %d for fb devs\n", FB_MAJOR); diff --git a/drivers/video/matrox/matroxfb_DAC1064.c b/drivers/video/matrox/matroxfb_DAC1064.c index db84a3000..e8a3738f8 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.c +++ b/drivers/video/matrox/matroxfb_DAC1064.c @@ -169,9 +169,9 @@ static void matroxfb_DAC1064_cursor(struct display* p, int mode, int x, int y) { if (mode == CM_ERASE) { if (ACCESS_FBINFO(cursor.state) != CM_ERASE) { + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); ACCESS_FBINFO(cursor.state) = CM_ERASE; - del_timer(&ACCESS_FBINFO(cursor.timer)); outDAC1064(PMINFO M1064_XCURCTRL, M1064_XCURCTRL_DIS); matroxfb_DAC_unlock_irqrestore(flags); } @@ -184,6 +184,7 @@ static void matroxfb_DAC1064_cursor(struct display* p, int mode, int x, int y) { y -= p->var.yoffset; if (p->var.vmode & FB_VMODE_DOUBLE) y *= 2; + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); if ((x != ACCESS_FBINFO(cursor.x)) || (y != ACCESS_FBINFO(cursor.y)) || ACCESS_FBINFO(cursor.redraw)) { ACCESS_FBINFO(cursor.redraw) = 0; @@ -339,16 +340,18 @@ void DAC1064_global_init(CPMINFO struct matrox_hw_state* hw) { #if defined(CONFIG_FB_MATROX_MAVEN) || defined(CONFIG_FB_MATROX_MAVEN_MODULE) if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) { hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT; - hw->DACreg[POS1064_XMISCCTRL] |= G400_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; - } else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) - hw->DACreg[POS1064_XMISCCTRL] |= G400_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12; + } else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12; + } else +#endif + if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12; else - hw->DACreg[POS1064_XMISCCTRL] |= G400_XMISCCTRL_MFC_DIS; + hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS; + if ((ACCESS_FBINFO(output.ph) | ACCESS_FBINFO(output.sh)) & MATROXFB_OUTPUT_CONN_PRIMARY) hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN; -#else - hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_MFC_DIS | M1064_XMISCCTRL_DAC_EN; -#endif } void DAC1064_global_restore(CPMINFO const struct matrox_hw_state* hw) { diff --git a/drivers/video/matrox/matroxfb_DAC1064.h b/drivers/video/matrox/matroxfb_DAC1064.h index 4a8fdb801..256a11d2c 100644 --- a/drivers/video/matrox/matroxfb_DAC1064.h +++ b/drivers/video/matrox/matroxfb_DAC1064.h @@ -84,10 +84,10 @@ void DAC1064_global_restore(CPMINFO const struct matrox_hw_state*); #define M1064_XMISCCTRL_MFC_VGA 0x00 #define M1064_XMISCCTRL_MFC_MAFC 0x02 #define M1064_XMISCCTRL_MFC_DIS 0x06 -#define G400_XMISCCTRL_MFC_MAFC 0x02 -#define G400_XMISCCTRL_MFC_PANELLINK 0x04 -#define G400_XMISCCTRL_MFC_DIS 0x06 -#define G400_XMISCCTRL_MFC_MASK 0x06 +#define GX00_XMISCCTRL_MFC_MAFC 0x02 +#define GX00_XMISCCTRL_MFC_PANELLINK 0x04 +#define GX00_XMISCCTRL_MFC_DIS 0x06 +#define GX00_XMISCCTRL_MFC_MASK 0x06 #define M1064_XMISCCTRL_DAC_6BIT 0x00 #define M1064_XMISCCTRL_DAC_8BIT 0x08 #define M1064_XMISCCTRL_DAC_WIDTHMASK 0x08 diff --git a/drivers/video/matrox/matroxfb_Ti3026.c b/drivers/video/matrox/matroxfb_Ti3026.c index 6bc3cea64..67dc556b0 100644 --- a/drivers/video/matrox/matroxfb_Ti3026.c +++ b/drivers/video/matrox/matroxfb_Ti3026.c @@ -352,9 +352,9 @@ static void matroxfb_ti3026_cursor(struct display* p, int mode, int x, int y) { if (mode == CM_ERASE) { if (ACCESS_FBINFO(cursor.state) != CM_ERASE) { + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); ACCESS_FBINFO(cursor.state) = CM_ERASE; - del_timer(&ACCESS_FBINFO(cursor.timer)); outTi3026(PMINFO TVP3026_XCURCTRL, ACCESS_FBINFO(currenthw->DACreg[POS3026_XCURCTRL])); matroxfb_DAC_unlock_irqrestore(flags); } @@ -367,6 +367,7 @@ static void matroxfb_ti3026_cursor(struct display* p, int mode, int x, int y) { y -= p->var.yoffset; if (p->var.vmode & FB_VMODE_DOUBLE) y *= 2; + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); matroxfb_DAC_lock_irqsave(flags); if ((x != ACCESS_FBINFO(cursor.x)) || (y != ACCESS_FBINFO(cursor.y)) || ACCESS_FBINFO(cursor.redraw)) { ACCESS_FBINFO(cursor.redraw) = 0; diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c index 040c79c2d..ef94171bc 100644 --- a/drivers/video/matrox/matroxfb_base.c +++ b/drivers/video/matrox/matroxfb_base.c @@ -97,7 +97,6 @@ #if defined(CONFIG_FB_OF) unsigned char nvram_read_byte(int); -int matrox_of_init(struct device_node *dp); static int default_vmode = VMODE_NVRAM; static int default_cmode = CMODE_NVRAM; #endif @@ -192,7 +191,7 @@ static void matroxfb_remove(WPMINFO int dummy) { } matroxfb_unregister_device(MINFO); unregister_framebuffer(&ACCESS_FBINFO(fbcon)); - del_timer(&ACCESS_FBINFO(cursor.timer)); + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); #ifdef CONFIG_MTRR if (ACCESS_FBINFO(mtrr.vram_valid)) mtrr_del(ACCESS_FBINFO(mtrr.vram), ACCESS_FBINFO(video.base), ACCESS_FBINFO(video.len)); @@ -827,7 +826,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, /* copy last setting... */ memcpy(hw, ohw, sizeof(*hw)); - del_timer(&ACCESS_FBINFO(cursor.timer)); + del_timer_sync(&ACCESS_FBINFO(cursor.timer)); ACCESS_FBINFO(cursor.state) = CM_ERASE; ACCESS_FBINFO(hw_switch->init(PMINFO hw, &mt, display)); @@ -845,7 +844,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, hw->CRTC[0x0C] = (pos & 0xFF00) >> 8; hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); hw->CRTCEXT[8] = pos >> 21; - if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); } @@ -856,7 +855,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, up_read(&ACCESS_FBINFO(altout.lock)); } ACCESS_FBINFO(hw_switch->restore(PMINFO hw, ohw, display)); - if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) ACCESS_FBINFO(primout)->program(MINFO, hw); } @@ -869,7 +868,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, ACCESS_FBINFO(cursor.redraw) = 1; ACCESS_FBINFO(currenthw) = hw; ACCESS_FBINFO(newhw) = ohw; - if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) { if (ACCESS_FBINFO(primout)) ACCESS_FBINFO(primout)->start(MINFO); } @@ -881,7 +880,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, } matrox_cfbX_init(PMINFO display); do_install_cmap(PMINFO display); -#if defined(CONFIG_FB_OF) && defined(CONFIG_FB_COMPAT_XPMAC) +#if defined(CONFIG_FB_COMPAT_XPMAC) if (console_fb_info == &ACCESS_FBINFO(fbcon)) { int vmode, cmode; @@ -899,7 +898,7 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con, display_info.cmap_data_address = 0; display_info.disp_reg_address = ACCESS_FBINFO(mmio.base); } -#endif /* CONFIG_FB_OF && CONFIG_FB_COMPAT_XPMAC */ +#endif /* CONFIG_FB_COMPAT_XPMAC */ } } return 0; @@ -1065,6 +1064,13 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file, up_read(&ACCESS_FBINFO(crtc2.lock)); } return 0; + case MATROXFB_OUTPUT_DFP: + if (!(ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP)) + return -ENXIO; + if (mom.mode!= MATROXFB_OUTPUT_MODE_MONITOR) + return -EINVAL; + /* mode did not change... */ + return 0; default: return -EINVAL; } @@ -1091,6 +1097,11 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file, if (val) return val; break; + case MATROXFB_OUTPUT_DFP: + if (!(ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP)) + return -ENXIO; + mom.mode = MATROXFB_OUTPUT_MODE_MONITOR; + break; default: return -EINVAL; } @@ -1106,6 +1117,12 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file, return -EINVAL; if (tmp & ACCESS_FBINFO(output.sh)) return -EINVAL; + if (tmp & MATROXFB_OUTPUT_CONN_DFP) { + if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY) + return -EINVAL; + if (ACCESS_FBINFO(output.sh)) + return -EINVAL; + } if (tmp == ACCESS_FBINFO(output.ph)) return 0; ACCESS_FBINFO(output.ph) = tmp; @@ -1122,6 +1139,10 @@ static int matroxfb_ioctl(struct inode *inode, struct file *file, u_int32_t tmp; tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.sh); + if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) + tmp &= ~MATROXFB_OUTPUT_CONN_SECONDARY; + if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) + tmp &= ~MATROXFB_OUTPUT_CONN_DFP; put_user_ret(tmp, (u_int32_t*)arg, -EFAULT); return 0; } @@ -1290,6 +1311,7 @@ static int sync = -1; /* "matrox:sync:xxxxx" */ static unsigned int fv = 0; /* "matrox:fv:xxxxx" */ static unsigned int fh = 0; /* "matrox:fh:xxxxxk" */ static unsigned int maxclk = 0; /* "matrox:maxclk:xxxxM" */ +static int dfp = 0; /* "matrox:dfp */ static char fontname[64]; /* "matrox:font:xxxxx" */ #ifndef MODULE @@ -1386,11 +1408,13 @@ static struct video_board vbG400 = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG4 #define DEVF_TEXT16B 0x0400 #define DEVF_CRTC2 0x0800 #define DEVF_MAVEN_CAPABLE 0x1000 +#define DEVF_PANELLINK_CAPABLE 0x2000 #define DEVF_GCORE (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2) +#define DEVF_G2CORE (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE) #define DEVF_G100 (DEVF_GCORE) /* no doc, no vxres... */ -#define DEVF_G200 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE) -#define DEVF_G400 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2) +#define DEVF_G200 (DEVF_G2CORE) +#define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2) static struct board { unsigned short vendor, device, rev, svid, sid; @@ -1586,6 +1610,12 @@ static int initMatrox2(WPMINFO struct display* d, struct board* b){ ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES); ACCESS_FBINFO(devflags.crtc2) = b->flags & DEVF_CRTC2; ACCESS_FBINFO(devflags.maven_capable) = b->flags & DEVF_MAVEN_CAPABLE; + if (b->flags & DEVF_PANELLINK_CAPABLE) { + ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_DFP; + if (dfp) + ACCESS_FBINFO(output.ph) |= MATROXFB_OUTPUT_CONN_DFP; + } + ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode); ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode); @@ -1791,7 +1821,7 @@ static int initMatrox2(WPMINFO struct display* d, struct board* b){ } /* FIXME: Where to move this?! */ -#if defined(CONFIG_FB_OF) +#if defined(CONFIG_PPC) #if defined(CONFIG_FB_COMPAT_XPMAC) strcpy(ACCESS_FBINFO(matrox_name), "MTRX,"); /* OpenFirmware naming convension */ strncat(ACCESS_FBINFO(matrox_name), b->name, 26); @@ -1817,7 +1847,7 @@ static int initMatrox2(WPMINFO struct display* d, struct board* b){ vesafb_defined = var; /* Note: mac_vmode_to_var() doesnot set all parameters */ } } -#endif +#endif /* CONFIG_PPC */ vesafb_defined.xres_virtual = vesafb_defined.xres; if (nopan) { vesafb_defined.yres_virtual = vesafb_defined.yres; @@ -2383,6 +2413,8 @@ int __init matroxfb_setup(char *options) { blink = value; else if (!strcmp(this_opt, "grayscale")) grayscale = value; + else if (!strcmp(this_opt, "dfp")) + dfp = value; else { strncpy(videomode, this_opt, sizeof(videomode)-1); } @@ -2407,21 +2439,6 @@ int __init matroxfb_init(void) return 0; } -#if defined(CONFIG_FB_OF) -int __init matrox_of_init(struct device_node *dp){ - DBG("matrox_of_init"); - - if (disabled) - return -ENXIO; - if (!initialized) { - initialized = 1; - matrox_init(); - } - /* failure? */ - return 0; -} -#endif /* CONFIG_FB_OF */ - #else /* *************************** init module code **************************** */ @@ -2500,6 +2517,8 @@ MODULE_PARM(grayscale, "i"); MODULE_PARM_DESC(grayscale, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)"); MODULE_PARM(cross4MB, "i"); MODULE_PARM_DESC(cross4MB, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)"); +MODULE_PARM(dfp, "i"); +MODULE_PARM_DESC(dfp, "Specifies whether to use digital flat panel interface of G200/G400 (0 or 1) (default=0)"); #ifdef CONFIG_FB_OF MODULE_PARM(vmode, "i"); MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)"); diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index 0cb21b0f9..c8a47fe9e 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -56,10 +56,10 @@ #include <video/fbcon-cfb24.h> #include <video/fbcon-cfb32.h> -#if defined(CONFIG_FB_OF) #if defined(CONFIG_FB_COMPAT_XPMAC) #include <asm/vc_ioctl.h> #endif +#if defined(CONFIG_PPC) #include <asm/prom.h> #include <asm/pci-bridge.h> #include <video/macmodes.h> @@ -544,7 +544,7 @@ struct matrox_fb_info { struct timer_list timer; } cursor; struct { unsigned red, green, blue, transp; } palette[256]; -#if defined(CONFIG_FB_OF) && defined(CONFIG_FB_COMPAT_XPMAC) +#if defined(CONFIG_FB_COMPAT_XPMAC) char matrox_name[32]; #endif /* These ifdefs must be last! They differ for module & non-module compiles */ diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c index 1219c0f2f..f4824762b 100644 --- a/drivers/video/matrox/matroxfb_crtc2.c +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -527,6 +527,10 @@ static int matroxfb_dh_ioctl(struct inode* inode, return -EINVAL; if (tmp & ACCESS_FBINFO(output.ph)) return -EINVAL; + if (tmp & MATROXFB_OUTPUT_CONN_DFP) + return -EINVAL; + if ((ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) && tmp) + return -EINVAL; if (tmp == ACCESS_FBINFO(output.sh)) return 0; ACCESS_FBINFO(output.sh) = tmp; @@ -542,7 +546,11 @@ static int matroxfb_dh_ioctl(struct inode* inode, { u_int32_t tmp; - tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.ph); + /* we do not support DFP from CRTC2 */ + tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.ph) & ~MATROXFB_OUTPUT_CONN_DFP; + /* CRTC1 in DFP mode disables CRTC2 at all (I know, I'm lazy) */ + if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) + tmp = 0; put_user_ret(tmp, (u_int32_t*)arg, -EFAULT); return 0; } @@ -675,6 +683,10 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { if (ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_SECONDARY) { ACCESS_FBINFO(output.sh) |= MATROXFB_OUTPUT_CONN_SECONDARY; ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_SECONDARY; + if (ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP) { + ACCESS_FBINFO(output.sh) &= ~MATROXFB_OUTPUT_CONN_DFP; + ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_DFP; + } } matroxfb_dh_set_var(&matroxfb_dh_defined, -2, &m2info->fbcon); diff --git a/drivers/video/offb.c b/drivers/video/offb.c index eebefb776..d98ab4e0e 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -275,9 +275,6 @@ extern void imsttfb_of_init(struct device_node *dp); #ifdef CONFIG_FB_CT65550 extern void chips_of_init(struct device_node *dp); #endif /* CONFIG_FB_CT65550 */ -#ifdef CONFIG_FB_MATROX -extern int matrox_of_init(struct device_node *dp); -#endif /* CONFIG_FB_MATROX */ #ifdef CONFIG_FB_CONTROL extern void control_of_init(struct device_node *dp); #endif /* CONFIG_FB_CONTROL */ @@ -411,12 +408,6 @@ static int __init offb_init_driver(struct device_node *dp) return 1; } #endif /* CONFIG_FB_CT65550 */ -#ifdef CONFIG_FB_MATROX - if (!strncmp(dp->name, "MTRX", 4)) { - matrox_of_init(dp); - return 1; - } -#endif /* CONFIG_FB_MATROX */ #ifdef CONFIG_FB_CONTROL if(!strcmp(dp->name, "control")) { control_of_init(dp); diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 4c2a13791..4b388f60b 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -402,7 +402,7 @@ static int __devinit riva_init_disp (struct rivafb_info *rinfo) disp->var = rivafb_default_var; info->disp = disp; -#warning FIXME: assure that disp->cmap is completely filled out + /* FIXME: assure that disp->cmap is completely filled out */ disp->screen_base = rinfo->fb_base; disp->visual = FB_VISUAL_PSEUDOCOLOR; @@ -727,7 +727,7 @@ static int rivafb_get_fix (struct fb_fix_screeninfo *fix, int con, fix->line_length = p->line_length; -#warning FIXME: set up MMIO region, export via FB_ACCEL_xxx + /* FIXME: set up MMIO region, export via FB_ACCEL_xxx */ fix->mmio_start = 0; fix->mmio_len = 0; fix->accel = FB_ACCEL_NONE; @@ -960,7 +960,7 @@ static int rivafb_set_var (struct fb_var_screeninfo *var, int con, dsp->type = FB_TYPE_PACKED_PIXELS; -#warning FIXME: verify that the above code sets dsp->* fields correctly + /* FIXME: verify that the above code sets dsp->* fields correctly */ memcpy (&dsp->var, &v, sizeof (v)); diff --git a/drivers/video/riva/riva_hw.c b/drivers/video/riva/riva_hw.c index 1bd904c8e..532f8c017 100644 --- a/drivers/video/riva/riva_hw.c +++ b/drivers/video/riva/riva_hw.c @@ -592,7 +592,7 @@ static void nv4CalcArbitration ) { int data, pagemiss, cas,width, video_enable, color_key_enable, bpp, align; - int nvclks, mclks, pclks, vpagemiss, crtpagemiss, vbs; + int nvclks, mclks, pclks, vpagemiss, crtpagemiss, vbs=0; int found, mclk_extra, mclk_loop, cbs, m1, p1; int mclk_freq, pclk_freq, nvclk_freq, mp_enable; int us_m, us_n, us_p, video_drain_rate, crtc_drain_rate; diff --git a/drivers/video/riva/riva_tbl.h b/drivers/video/riva/riva_tbl.h index 8188c0fd8..23e7cb75b 100644 --- a/drivers/video/riva/riva_tbl.h +++ b/drivers/video/riva/riva_tbl.h @@ -60,6 +60,8 @@ static unsigned RivaTablePTIMER[][2] = {0x00000050, 0x00000000}, {0x00000040, 0xFFFFFFFF} }; + +#if 0 static unsigned RivaTableFIFO[][2] = { {0x00000000, 0x80000000}, @@ -70,6 +72,8 @@ static unsigned RivaTableFIFO[][2] = {0x00002800, 0x80000012}, {0x00003800, 0x80000013} }; +#endif + static unsigned nv3TablePFIFO[][2] = { {0x00000140, 0x00000000}, diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 499d8fef7..bbd547454 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -86,23 +86,7 @@ #include <video/fbcon-cfb24.h> #include <video/fbcon-cfb32.h> -#ifndef LINUX_VERSION_CODE -#include <linux/version.h> -#endif - -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) -#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) -/* nothing? */ -#else #include <linux/spinlock.h> -#endif /* membase0 register offsets */ #define STATUS 0x00 @@ -332,10 +316,10 @@ struct fb_info_tdfx { u32 max_pixclock; unsigned long regbase_phys; - unsigned long regbase_virt; + void *regbase_virt; unsigned long regbase_size; unsigned long bufbase_phys; - unsigned long bufbase_virt; + void *bufbase_virt; unsigned long bufbase_size; unsigned long iobase; @@ -368,6 +352,9 @@ struct fb_info_tdfx { } cursor; spinlock_t DAClock; +#ifdef CONFIG_MTRR + int mtrr_idx; +#endif }; /* @@ -468,11 +455,7 @@ static unsigned long do_lfb_size(void); /* * Interface used by the world */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) -void tdfxfb_init(void); -#else int tdfxfb_init(void); -#endif void tdfxfb_setup(char *options, int *ints); @@ -505,71 +488,8 @@ struct mode default_mode[] = { 0, FB_VMODE_NONINTERLACED } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - , - { "800x600-8@56", /* @ 56 Hz */ - { - 800, 600, 800, 600, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 27778, 128, 24, 22, 1, 72, 2, - 0, FB_VMODE_NONINTERLACED - } - }, - { "1024x768-8@60", /* @ 60 Hz */ - { - 1024, 768, 1024, 768, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 15385, 168, 8, 29, 3, 144, 6, - 0, FB_VMODE_NONINTERLACED - } - }, - { "1280x1024-8@61", /* @ 61 Hz */ - { - 1280, 1024, 1280, 1024, 0, 0, 8, 0, - {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 9091, 200, 48, 26, 1, 184, 3, - 0, FB_VMODE_NONINTERLACED - } - }, - { "1024x768-16@60", /* @ 60 Hz */ /* basically for testing */ - { - 1024, 768, 1024, 768, 0, 0, 16, 0, - {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 15385, 168, 8, 29, 3, 144, 6, - 0, FB_VMODE_NONINTERLACED - } - }, - { "1024x768-24@60", /* @ 60 Hz */ - { - 1024, 768, 1024, 768, 0, 0, 24, 0, - {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 15385, 168, 8, 29, 3, 144, 6, - 0, FB_VMODE_NONINTERLACED - } - }, - { "1024x768-32@60", /* @ 60 Hz */ - { - 1024, 768, 1024, 768, 0, 0, 32, 0, - {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, - 0, FB_ACTIVATE_NOW, -1, -1, FB_ACCELF_TEXT, - 15385, 168, 8, 29, 3, 144, 6, - 0, FB_VMODE_NONINTERLACED - } - } - -#endif }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) -static int modes = sizeof(default_mode)/sizeof(struct mode); -static int default_mode_index = 0; -#endif - static struct fb_info_tdfx fb_info; static int noaccel = 0; @@ -1674,17 +1594,10 @@ static int tdfxfb_encode_fix(struct fb_fix_screeninfo* fix, info->dev == PCI_DEVICE_ID_3DFX_BANSHEE ? "3Dfx Banshee" : "3Dfx Voodoo3"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - fix->smem_start = (char*)info->bufbase_phys; - fix->smem_len = info->bufbase_size; - fix->mmio_start = (char*)info->regbase_phys; - fix->mmio_len = info->regbase_size; -#else fix->smem_start = info->bufbase_phys; fix->smem_len = info->bufbase_size; fix->mmio_start = info->regbase_phys; fix->mmio_len = info->regbase_size; -#endif fix->accel = FB_ACCEL_3DFX_BANSHEE; fix->type = FB_TYPE_PACKED_PIXELS; fix->type_aux = 0; @@ -1815,7 +1728,7 @@ static int tdfxfb_set_var(struct fb_var_screeninfo *var, struct fb_fix_screeninfo fix; tdfxfb_encode_fix(&fix, &par, info); - display->screen_base = (char *)info->bufbase_virt; + display->screen_base = info->bufbase_virt; display->visual = fix.visual; display->type = fix.type; display->type_aux = fix.type_aux; @@ -1940,20 +1853,10 @@ static int tdfxfb_ioctl(struct inode *inode, return -EINVAL; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) -__initfunc(void tdfxfb_init(void)) { -#else int __init tdfxfb_init(void) { -#endif struct pci_dev *pdev = NULL; struct fb_var_screeninfo var; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - if(!pcibios_present()) return; -#else - if(!pcibios_present()) return -ENXIO; -#endif - while ((pdev = pci_find_device(PCI_VENDOR_ID_3DFX, PCI_ANY_ID, pdev))) { if(((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && ((pdev->device == PCI_DEVICE_ID_3DFX_BANSHEE) || @@ -1968,67 +1871,38 @@ int __init tdfxfb_init(void) { ? BANSHEE_MAX_PIXCLOCK : VOODOO3_MAX_PIXCLOCK; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - fb_info.regbase_phys = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; - fb_info.regbase_size = 1 << 24; - fb_info.regbase_virt = - (unsigned long)ioremap_nocache(fb_info.regbase_phys, 1 << 24); - if(!fb_info.regbase_virt) { - printk("fb: Can't remap %s register area.\n", name); - return; - } - - fb_info.bufbase_phys = pdev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK; - if(!(fb_info.bufbase_size = do_lfb_size())) { - printk("fb: Can't count %s memory.\n", name); - iounmap((void*)fb_info.regbase_virt); - return; - } - fb_info.bufbase_virt = - (unsigned long)ioremap_nocache(fb_info.bufbase_phys, fb_info.bufbase_size); - if(!fb_info.regbase_virt) { - printk("fb: Can't remap %s framebuffer.\n", name); - iounmap((void*)fb_info.regbase_virt); - return; - } - - fb_info.iobase = pdev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; -#else fb_info.regbase_phys = pci_resource_start(pdev, 0); fb_info.regbase_size = 1 << 24; - fb_info.regbase_virt = - (unsigned long)ioremap_nocache(fb_info.regbase_phys, 1 << 24); + fb_info.regbase_virt = ioremap_nocache(fb_info.regbase_phys, 1 << 24); if(!fb_info.regbase_virt) { printk("fb: Can't remap %s register area.\n", name); return -ENXIO; } - fb_info.bufbase_phys = pdev->resource[1].start; + fb_info.bufbase_phys = pci_resource_start (pdev, 1); if(!(fb_info.bufbase_size = do_lfb_size())) { - iounmap((void*)fb_info.regbase_virt); + iounmap(fb_info.regbase_virt); printk("fb: Can't count %s memory.\n", name); return -ENXIO; } - fb_info.bufbase_virt = - (unsigned long)ioremap_nocache(fb_info.bufbase_phys, fb_info.bufbase_size); + fb_info.bufbase_virt = ioremap_nocache(fb_info.bufbase_phys, fb_info.bufbase_size); if(!fb_info.regbase_virt) { printk("fb: Can't remap %s framebuffer.\n", name); - iounmap((void*)fb_info.regbase_virt); + iounmap(fb_info.regbase_virt); return -ENXIO; } - fb_info.iobase = pdev->resource[2].start; -#endif + fb_info.iobase = pci_resource_start (pdev, 2); printk("fb: %s memory = %ldK\n", name, fb_info.bufbase_size >> 10); #ifdef CONFIG_MTRR if (!nomtrr) { - if (mtrr_add(fb_info.bufbase_phys, fb_info.bufbase_size, - MTRR_TYPE_WRCOMB, 1)>=0) + fb_info.mtrr_idx = mtrr_add(fb_info.bufbase_phys, fb_info.bufbase_size, + MTRR_TYPE_WRCOMB, 1); printk("fb: MTRR's turned on\n"); } -#endif +#endif /* clear framebuffer memory */ memset_io(fb_info.bufbase_virt, 0, fb_info.bufbase_size); @@ -2053,16 +1927,10 @@ int __init tdfxfb_init(void) { fb_info.fb_info.blank = &tdfxfb_blank; fb_info.fb_info.flags = FBINFO_FLAG_DEFAULT; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - var = default_mode[default_mode_index < modes - ? default_mode_index - : 0].var; -#else memset(&var, 0, sizeof(var)); if(!mode_option || !fb_find_mode(&var, &fb_info.fb_info, mode_option, NULL, 0, NULL, 8)) var = default_mode[0].var; -#endif if(noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; else var.accel_flags |= FB_ACCELF_TEXT; @@ -2081,57 +1949,73 @@ int __init tdfxfb_init(void) { if(tdfxfb_decode_var(&var, &fb_info.default_par, &fb_info)) { /* this is getting really bad!... */ printk("tdfxfb: can't decode default video mode\n"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - return; -#else return -ENXIO; -#endif } } - fb_info.disp.screen_base = (void*)fb_info.bufbase_virt; + fb_info.disp.screen_base = fb_info.bufbase_virt; fb_info.disp.var = var; if(tdfxfb_set_var(&var, -1, &fb_info.fb_info)) { printk("tdfxfb: can't set default video mode\n"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - return; -#else return -ENXIO; -#endif } if(register_framebuffer(&fb_info.fb_info) < 0) { printk("tdfxfb: can't register framebuffer\n"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - return; -#else return -ENXIO; -#endif } printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.fb_info.node), fb_info.fb_info.modename); + /* FIXME: module cannot be unloaded */ + /* verify tdfxfb_exit before removing this */ MOD_INC_USE_COUNT; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - return; -#else return 0; -#endif } } /* hmm, no frame suitable buffer found ... */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - return; -#else return -ENXIO; +} + +/** + * tdfxfb_exit - Driver cleanup + * + * Releases all resources allocated during the + * course of the driver's lifetime. + * + * FIXME - do results of fb_alloc_cmap need disposal? + */ +static void __exit tdfxfb_exit (void) +{ + unregister_framebuffer(&fb_info.fb_info); + del_timer_sync(&fb_info.cursor.timer); + +#ifdef CONFIG_MTRR + if (!nomtrr) { + mtrr_del(fb_info.mtrr_idx, fb_info.bufbase_phys, fb_info.bufbase_size); + printk("fb: MTRR's turned off\n"); + } #endif + + iounmap(fb_info.regbase_virt); + iounmap(fb_info.bufbase_virt); } +MODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>"); +MODULE_DESCRIPTION("3Dfx framebuffer device driver"); + +#ifdef MODULE +module_init(tdfxfb_init); +#endif +module_exit(tdfxfb_exit); + + +#ifndef MODULE void tdfxfb_setup(char *options, int *ints) { char* this_opt; @@ -2160,19 +2044,11 @@ void tdfxfb_setup(char *options, } else if (!strncmp(this_opt, "font:", 5)) { strncpy(fontname, this_opt + 5, 40); } else { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) - int i; - for(i = 0; i < modes; i++) { - if(!strcmp(this_opt, default_mode[i].name)) { - default_mode_index = i; - } - } -#else mode_option = this_opt; -#endif } } } +#endif static int tdfxfb_switch_con(int con, struct fb_info *fb) { @@ -2438,7 +2314,7 @@ static void tdfxfb_hwcursor_init(void) start = (fb_info.bufbase_size-1024) & PAGE_MASK; fb_info.bufbase_size=start; fb_info.cursor.cursorimage=fb_info.bufbase_size; - printk("tdfxfb: reserving 1024 bytes for the hwcursor at 0x%08lx\n", + printk("tdfxfb: reserving 1024 bytes for the hwcursor at %p\n", fb_info.regbase_virt+fb_info.cursor.cursorimage); } |