2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2004 Intel Corporation < naveen . b . s @ intel . com >
*
* All rights reserved .
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*
* ACPI based HotPlug driver that supports Memory Hotplug
* This driver fields notifications from firmare for memory add
* and remove operations and alerts the VM of the affected memory
* ranges .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/memory_hotplug.h>
# include <acpi/acpi_drivers.h>
# define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000UL
# define ACPI_MEMORY_DEVICE_CLASS "memory"
# define ACPI_MEMORY_DEVICE_HID "PNP0C80"
# define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device"
# define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT
2007-02-13 06:42:12 +03:00
ACPI_MODULE_NAME ( " acpi_memhotplug " ) ;
MODULE_AUTHOR ( " Naveen B S <naveen.b.s@intel.com> " ) ;
2007-02-13 07:50:02 +03:00
MODULE_DESCRIPTION ( " Hotplug Mem Driver " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
/* Memory Device States */
# define MEMORY_INVALID_STATE 0
# define MEMORY_POWER_ON_STATE 1
# define MEMORY_POWER_OFF_STATE 2
2005-08-05 08:44:28 +04:00
static int acpi_memory_device_add ( struct acpi_device * device ) ;
static int acpi_memory_device_remove ( struct acpi_device * device , int type ) ;
2006-06-27 13:53:28 +04:00
static int acpi_memory_device_start ( struct acpi_device * device ) ;
2005-04-17 02:20:36 +04:00
2007-07-23 16:44:41 +04:00
static const struct acpi_device_id memory_device_ids [ ] = {
{ ACPI_MEMORY_DEVICE_HID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , memory_device_ids ) ;
2005-04-17 02:20:36 +04:00
static struct acpi_driver acpi_memory_device_driver = {
2007-02-13 07:33:40 +03:00
. name = " acpi_memhotplug " ,
2005-08-05 08:44:28 +04:00
. class = ACPI_MEMORY_DEVICE_CLASS ,
2007-07-23 16:44:41 +04:00
. ids = memory_device_ids ,
2005-08-05 08:44:28 +04:00
. ops = {
. add = acpi_memory_device_add ,
. remove = acpi_memory_device_remove ,
2006-06-27 13:53:28 +04:00
. start = acpi_memory_device_start ,
2005-08-05 08:44:28 +04:00
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-06-27 13:53:27 +04:00
struct acpi_memory_info {
struct list_head list ;
u64 start_addr ; /* Memory Range start physical addr */
u64 length ; /* Memory Range length */
unsigned short caching ; /* memory cache attribute */
unsigned short write_protect ; /* memory read/write attribute */
unsigned int enabled : 1 ;
} ;
2005-04-17 02:20:36 +04:00
struct acpi_memory_device {
2006-05-20 00:54:38 +04:00
struct acpi_device * device ;
2005-08-05 08:44:28 +04:00
unsigned int state ; /* State of the memory device */
2006-06-27 13:53:27 +04:00
struct list_head res_list ;
2005-04-17 02:20:36 +04:00
} ;
2006-10-20 10:28:31 +04:00
static int acpi_hotmem_initialized ;
2006-06-27 13:53:27 +04:00
static acpi_status
acpi_memory_get_resource ( struct acpi_resource * resource , void * context )
{
struct acpi_memory_device * mem_device = context ;
struct acpi_resource_address64 address64 ;
struct acpi_memory_info * info , * new ;
acpi_status status ;
status = acpi_resource_to_address64 ( resource , & address64 ) ;
if ( ACPI_FAILURE ( status ) | |
( address64 . resource_type ! = ACPI_MEMORY_RANGE ) )
return AE_OK ;
list_for_each_entry ( info , & mem_device - > res_list , list ) {
/* Can we combine the resource range information? */
if ( ( info - > caching = = address64 . info . mem . caching ) & &
( info - > write_protect = = address64 . info . mem . write_protect ) & &
( info - > start_addr + info - > length = = address64 . minimum ) ) {
info - > length + = address64 . address_length ;
return AE_OK ;
}
}
new = kzalloc ( sizeof ( struct acpi_memory_info ) , GFP_KERNEL ) ;
if ( ! new )
return AE_ERROR ;
INIT_LIST_HEAD ( & new - > list ) ;
new - > caching = address64 . info . mem . caching ;
new - > write_protect = address64 . info . mem . write_protect ;
new - > start_addr = address64 . minimum ;
new - > length = address64 . address_length ;
list_add_tail ( & new - > list , & mem_device - > res_list ) ;
return AE_OK ;
}
2005-04-17 02:20:36 +04:00
static int
acpi_memory_get_device_resources ( struct acpi_memory_device * mem_device )
{
acpi_status status ;
2006-06-27 13:53:27 +04:00
struct acpi_memory_info * info , * n ;
2005-04-17 02:20:36 +04:00
2006-08-05 23:15:04 +04:00
if ( ! list_empty ( & mem_device - > res_list ) )
return 0 ;
2006-05-20 00:54:41 +04:00
status = acpi_walk_resources ( mem_device - > device - > handle , METHOD_NAME__CRS ,
2006-06-27 13:53:27 +04:00
acpi_memory_get_resource , mem_device ) ;
if ( ACPI_FAILURE ( status ) ) {
list_for_each_entry_safe ( info , n , & mem_device - > res_list , list )
kfree ( info ) ;
2006-08-05 23:15:04 +04:00
INIT_LIST_HEAD ( & mem_device - > res_list ) ;
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
acpi_memory_get_device ( acpi_handle handle ,
2005-08-05 08:44:28 +04:00
struct acpi_memory_device * * mem_device )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
acpi_handle phandle ;
struct acpi_device * device = NULL ;
struct acpi_device * pdevice = NULL ;
if ( ! acpi_bus_get_device ( handle , & device ) & & device )
goto end ;
status = acpi_get_parent ( handle , & phandle ) ;
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Cannot find acpi parent " ) ) ;
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
/* Get the parent device */
status = acpi_bus_get_device ( phandle , & pdevice ) ;
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Cannot get acpi bus device " ) ) ;
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
/*
* Now add the notified device . This creates the acpi_device
* and invokes . add function
*/
status = acpi_bus_add ( & device , pdevice , handle , ACPI_BUS_TYPE_DEVICE ) ;
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " Cannot add acpi bus " ) ) ;
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
* mem_device = acpi_driver_data ( device ) ;
if ( ! ( * mem_device ) ) {
2005-08-05 08:44:28 +04:00
printk ( KERN_ERR " \n driver data not found " ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_check_device ( struct acpi_memory_device * mem_device )
2005-04-17 02:20:36 +04:00
{
unsigned long current_status ;
/* Get device present/absent information from the _STA */
2006-05-20 00:54:41 +04:00
if ( ACPI_FAILURE ( acpi_evaluate_integer ( mem_device - > device - > handle , " _STA " ,
2005-08-05 08:44:28 +04:00
NULL , & current_status ) ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
/*
* Check for device status . Device should be
* present / enabled / functioning .
*/
2007-04-25 22:17:39 +04:00
if ( ! ( ( current_status & ACPI_STA_DEVICE_PRESENT )
& & ( current_status & ACPI_STA_DEVICE_ENABLED )
& & ( current_status & ACPI_STA_DEVICE_FUNCTIONING ) ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_enable_device ( struct acpi_memory_device * mem_device )
2005-04-17 02:20:36 +04:00
{
2006-06-27 13:53:27 +04:00
int result , num_enabled = 0 ;
struct acpi_memory_info * info ;
2006-06-27 13:53:31 +04:00
int node ;
2005-04-17 02:20:36 +04:00
/* Get the range from the _CRS */
result = acpi_memory_get_device_resources ( mem_device ) ;
if ( result ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " get_device_resources failed \n " ) ;
2005-04-17 02:20:36 +04:00
mem_device - > state = MEMORY_INVALID_STATE ;
return result ;
}
2006-05-20 00:54:41 +04:00
node = acpi_get_node ( mem_device - > device - > handle ) ;
2005-04-17 02:20:36 +04:00
/*
* Tell the VM there is more memory here . . .
* Note : Assume that this function returns zero on success
2006-06-27 13:53:27 +04:00
* We don ' t have memory - hot - add rollback function , now .
* ( i . e . memory - hot - remove function )
2005-04-17 02:20:36 +04:00
*/
2006-06-27 13:53:27 +04:00
list_for_each_entry ( info , & mem_device - > res_list , list ) {
2006-08-05 23:15:02 +04:00
if ( info - > enabled ) { /* just sanity check...*/
2006-06-27 13:53:29 +04:00
num_enabled + + ;
continue ;
}
2006-10-01 10:27:07 +04:00
if ( node < 0 )
node = memory_add_physaddr_to_nid ( info - > start_addr ) ;
2006-06-27 13:53:30 +04:00
result = add_memory ( node , info - > start_addr , info - > length ) ;
2006-06-27 13:53:27 +04:00
if ( result )
continue ;
info - > enabled = 1 ;
num_enabled + + ;
}
if ( ! num_enabled ) {
2006-06-30 11:15:00 +04:00
printk ( KERN_ERR PREFIX " add_memory failed \n " ) ;
2005-04-17 02:20:36 +04:00
mem_device - > state = MEMORY_INVALID_STATE ;
2006-06-27 13:53:27 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
return result ;
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_powerdown_device ( struct acpi_memory_device * mem_device )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
2005-08-05 08:44:28 +04:00
struct acpi_object_list arg_list ;
2005-04-17 02:20:36 +04:00
union acpi_object arg ;
unsigned long current_status ;
/* Issue the _EJ0 command */
arg_list . count = 1 ;
arg_list . pointer = & arg ;
arg . type = ACPI_TYPE_INTEGER ;
arg . integer . value = 1 ;
2006-05-20 00:54:41 +04:00
status = acpi_evaluate_object ( mem_device - > device - > handle ,
2005-08-05 08:44:28 +04:00
" _EJ0 " , & arg_list , NULL ) ;
2005-04-17 02:20:36 +04:00
/* Return on _EJ0 failure */
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " _EJ0 failed " ) ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
/* Evalute _STA to check if the device is disabled */
2006-05-20 00:54:41 +04:00
status = acpi_evaluate_integer ( mem_device - > device - > handle , " _STA " ,
2005-08-05 08:44:28 +04:00
NULL , & current_status ) ;
2005-04-17 02:20:36 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
/* Check for device status. Device should be disabled */
2007-04-25 22:17:39 +04:00
if ( current_status & ACPI_STA_DEVICE_ENABLED )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_disable_device ( struct acpi_memory_device * mem_device )
2005-04-17 02:20:36 +04:00
{
int result ;
2006-06-27 13:53:27 +04:00
struct acpi_memory_info * info , * n ;
2005-04-17 02:20:36 +04:00
/*
* Ask the VM to offline this memory range .
* Note : Assume that this function returns zero on success
*/
2006-06-27 13:53:27 +04:00
list_for_each_entry_safe ( info , n , & mem_device - > res_list , list ) {
if ( info - > enabled ) {
result = remove_memory ( info - > start_addr , info - > length ) ;
if ( result )
return result ;
}
kfree ( info ) ;
2005-04-17 02:20:36 +04:00
}
/* Power-off and eject the device */
result = acpi_memory_powerdown_device ( mem_device ) ;
if ( result ) {
/* Set the status of the device to invalid */
mem_device - > state = MEMORY_INVALID_STATE ;
return result ;
}
mem_device - > state = MEMORY_POWER_OFF_STATE ;
return result ;
}
2005-08-05 08:44:28 +04:00
static void acpi_memory_device_notify ( acpi_handle handle , u32 event , void * data )
2005-04-17 02:20:36 +04:00
{
struct acpi_memory_device * mem_device ;
struct acpi_device * device ;
switch ( event ) {
case ACPI_NOTIFY_BUS_CHECK :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" \n Received BUS CHECK notification for device \n " ) ) ;
2005-04-17 02:20:36 +04:00
/* Fall Through */
case ACPI_NOTIFY_DEVICE_CHECK :
if ( event = = ACPI_NOTIFY_DEVICE_CHECK )
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" \n Received DEVICE CHECK notification for device \n " ) ) ;
2005-04-17 02:20:36 +04:00
if ( acpi_memory_get_device ( handle , & mem_device ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Cannot find driver data \n " ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
if ( ! acpi_memory_check_device ( mem_device ) ) {
if ( acpi_memory_enable_device ( mem_device ) )
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX
" Cannot enable memory device \n " ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case ACPI_NOTIFY_EJECT_REQUEST :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" \n Received EJECT REQUEST notification for device \n " ) ) ;
2005-04-17 02:20:36 +04:00
if ( acpi_bus_get_device ( handle , & device ) ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Device doesn't exist \n " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
mem_device = acpi_driver_data ( device ) ;
if ( ! mem_device ) {
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX " Driver Data is NULL \n " ) ;
2005-04-17 02:20:36 +04:00
break ;
}
/*
* Currently disabling memory device from kernel mode
* TBD : Can also be disabled from user mode scripts
* TBD : Can also be disabled by Callback registration
2005-08-05 08:44:28 +04:00
* with generic sysfs driver
2005-04-17 02:20:36 +04:00
*/
if ( acpi_memory_disable_device ( mem_device ) )
2006-06-27 07:41:38 +04:00
printk ( KERN_ERR PREFIX
" Disable memory device \n " ) ;
2005-04-17 02:20:36 +04:00
/*
* TBD : Invoke acpi_bus_remove to cleanup data structures
*/
break ;
default :
ACPI_DEBUG_PRINT ( ( ACPI_DB_INFO ,
2005-08-05 08:44:28 +04:00
" Unsupported event [0x%x] \n " , event ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_device_add ( struct acpi_device * device )
2005-04-17 02:20:36 +04:00
{
int result ;
struct acpi_memory_device * mem_device = NULL ;
if ( ! device )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-12-19 23:56:11 +03:00
mem_device = kzalloc ( sizeof ( struct acpi_memory_device ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem_device )
2006-06-27 08:41:40 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2006-06-27 13:53:27 +04:00
INIT_LIST_HEAD ( & mem_device - > res_list ) ;
2006-05-20 00:54:38 +04:00
mem_device - > device = device ;
2005-04-17 02:20:36 +04:00
sprintf ( acpi_device_name ( device ) , " %s " , ACPI_MEMORY_DEVICE_NAME ) ;
sprintf ( acpi_device_class ( device ) , " %s " , ACPI_MEMORY_DEVICE_CLASS ) ;
acpi_driver_data ( device ) = mem_device ;
/* Get the range from the _CRS */
result = acpi_memory_get_device_resources ( mem_device ) ;
if ( result ) {
kfree ( mem_device ) ;
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
/* Set the device state */
mem_device - > state = MEMORY_POWER_ON_STATE ;
2006-10-20 10:28:30 +04:00
printk ( KERN_DEBUG " %s \n " , acpi_device_name ( device ) ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return result ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int acpi_memory_device_remove ( struct acpi_device * device , int type )
2005-04-17 02:20:36 +04:00
{
struct acpi_memory_device * mem_device = NULL ;
if ( ! device | | ! acpi_driver_data ( device ) )
2006-06-27 08:41:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-10-01 02:28:50 +04:00
mem_device = acpi_driver_data ( device ) ;
2005-04-17 02:20:36 +04:00
kfree ( mem_device ) ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 13:53:28 +04:00
static int acpi_memory_device_start ( struct acpi_device * device )
{
struct acpi_memory_device * mem_device ;
int result = 0 ;
2006-10-20 10:28:31 +04:00
/*
* Early boot code has recognized memory area by EFI / E820 .
* If DSDT shows these memory devices on boot , hotplug is not necessary
* for them . So , it just returns until completion of this driver ' s
* start up .
*/
if ( ! acpi_hotmem_initialized )
return 0 ;
2006-06-27 13:53:28 +04:00
mem_device = acpi_driver_data ( device ) ;
if ( ! acpi_memory_check_device ( mem_device ) ) {
/* call add_memory func */
result = acpi_memory_enable_device ( mem_device ) ;
if ( result )
ACPI_DEBUG_PRINT ( ( ACPI_DB_ERROR ,
" Error in acpi_memory_enable_device \n " ) ) ;
}
2006-06-30 03:57:46 +04:00
return result ;
2006-06-27 13:53:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Helper function to check for memory device
*/
2005-08-05 08:44:28 +04:00
static acpi_status is_memory_device ( acpi_handle handle )
2005-04-17 02:20:36 +04:00
{
char * hardware_id ;
acpi_status status ;
2005-08-05 08:44:28 +04:00
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER , NULL } ;
2005-04-17 02:20:36 +04:00
struct acpi_device_info * info ;
status = acpi_get_object_info ( handle , & buffer ) ;
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
info = buffer . pointer ;
if ( ! ( info - > valid & ACPI_VALID_HID ) ) {
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2006-06-27 08:41:40 +04:00
return AE_ERROR ;
2005-04-17 02:20:36 +04:00
}
hardware_id = info - > hardware_id . value ;
if ( ( hardware_id = = NULL ) | |
2005-08-05 08:44:28 +04:00
( strcmp ( hardware_id , ACPI_MEMORY_DEVICE_HID ) ) )
2005-04-17 02:20:36 +04:00
status = AE_ERROR ;
2006-06-30 11:19:10 +04:00
kfree ( buffer . pointer ) ;
2006-06-27 08:41:40 +04:00
return status ;
2005-04-17 02:20:36 +04:00
}
static acpi_status
2005-08-05 08:44:28 +04:00
acpi_memory_register_notify_handler ( acpi_handle handle ,
u32 level , void * ctxt , void * * retv )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
status = is_memory_device ( handle ) ;
2006-08-15 09:37:32 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return AE_OK ; /* continue */
2005-04-17 02:20:36 +04:00
status = acpi_install_notify_handler ( handle , ACPI_SYSTEM_NOTIFY ,
2005-08-05 08:44:28 +04:00
acpi_memory_device_notify , NULL ) ;
2006-06-27 07:58:43 +04:00
/* continue */
2006-06-27 08:41:40 +04:00
return AE_OK ;
2005-04-17 02:20:36 +04:00
}
static acpi_status
2005-08-05 08:44:28 +04:00
acpi_memory_deregister_notify_handler ( acpi_handle handle ,
u32 level , void * ctxt , void * * retv )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
status = is_memory_device ( handle ) ;
2006-08-15 09:37:32 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 08:41:40 +04:00
return AE_OK ; /* continue */
2005-04-17 02:20:36 +04:00
status = acpi_remove_notify_handler ( handle ,
2005-08-05 08:44:28 +04:00
ACPI_SYSTEM_NOTIFY ,
acpi_memory_device_notify ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 08:41:40 +04:00
return AE_OK ; /* continue */
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static int __init acpi_memory_device_init ( void )
2005-04-17 02:20:36 +04:00
{
int result ;
acpi_status status ;
result = acpi_bus_register_driver ( & acpi_memory_device_driver ) ;
if ( result < 0 )
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
2005-08-05 08:44:28 +04:00
ACPI_UINT32_MAX ,
acpi_memory_register_notify_handler ,
NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) ) {
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " walk_namespace failed " ) ) ;
2005-04-17 02:20:36 +04:00
acpi_bus_unregister_driver ( & acpi_memory_device_driver ) ;
2006-06-27 08:41:40 +04:00
return - ENODEV ;
2005-08-05 08:44:28 +04:00
}
2005-04-17 02:20:36 +04:00
2006-10-20 10:28:31 +04:00
acpi_hotmem_initialized = 1 ;
2006-06-27 08:41:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static void __exit acpi_memory_device_exit ( void )
2005-04-17 02:20:36 +04:00
{
acpi_status status ;
/*
* Adding this to un - install notification handlers for all the device
* handles .
*/
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_ROOT_OBJECT ,
2005-08-05 08:44:28 +04:00
ACPI_UINT32_MAX ,
acpi_memory_deregister_notify_handler ,
NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
if ( ACPI_FAILURE ( status ) )
2006-06-27 07:58:43 +04:00
ACPI_EXCEPTION ( ( AE_INFO , status , " walk_namespace failed " ) ) ;
2005-04-17 02:20:36 +04:00
acpi_bus_unregister_driver ( & acpi_memory_device_driver ) ;
2006-06-27 08:41:40 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
module_init ( acpi_memory_device_init ) ;
module_exit ( acpi_memory_device_exit ) ;