11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com> 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * All rights reserved. 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation; either version 2 of the License, or (at 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * your option) any later version. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful, but 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NON INFRINGEMENT. See the GNU General Public License for more 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * details. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ACPI based HotPlug driver that supports Memory Hotplug 23c7060d9e9e5aceaddaa58df87221befff963ec3fNick Andrew * This driver fields notifications from firmware for memory add 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and remove operations and alerts the VM of the affected memory 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ranges. 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/memory_hotplug.h> 335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <acpi/acpi_drivers.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACPI_MEMORY_DEVICE_CLASS "memory" 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACPI_MEMORY_DEVICE_HID "PNP0C80" 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 42aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui#undef PREFIX 43aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui#define PREFIX "ACPI:memory_hp:" 44aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui 45f52fd66d2ea794010c2d7536cf8e6abed0ac4947Len BrownACPI_MODULE_NAME("acpi_memhotplug"); 46f52fd66d2ea794010c2d7536cf8e6abed0ac4947Len BrownMODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); 477cda93e008e1a477970adbf82dba81a5d4f0ae40Len BrownMODULE_DESCRIPTION("Hotplug Mem Driver"); 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Memory Device States */ 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MEMORY_INVALID_STATE 0 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MEMORY_POWER_ON_STATE 1 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MEMORY_POWER_OFF_STATE 2 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 554be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_device_add(struct acpi_device *device); 564be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_device_remove(struct acpi_device *device, int type); 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 581ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renningerstatic const struct acpi_device_id memory_device_ids[] = { 591ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renninger {ACPI_MEMORY_DEVICE_HID, 0}, 601ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renninger {"", 0}, 611ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renninger}; 621ba90e3a87c46500623afdc3898573e4a5ebb21bThomas RenningerMODULE_DEVICE_TABLE(acpi, memory_device_ids); 631ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renninger 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct acpi_driver acpi_memory_device_driver = { 65c2b6705b75d9c7aff98a4602a32230639e10891cLen Brown .name = "acpi_memhotplug", 664be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown .class = ACPI_MEMORY_DEVICE_CLASS, 671ba90e3a87c46500623afdc3898573e4a5ebb21bThomas Renninger .ids = memory_device_ids, 684be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown .ops = { 694be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown .add = acpi_memory_device_add, 704be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown .remove = acpi_memory_device_remove, 714be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown }, 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 749ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyukistruct acpi_memory_info { 759ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct list_head list; 769ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki u64 start_addr; /* Memory Range start physical addr */ 779ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki u64 length; /* Memory Range length */ 789ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki unsigned short caching; /* memory cache attribute */ 799ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki unsigned short write_protect; /* memory read/write attribute */ 809ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki unsigned int enabled:1; 819ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki}; 829ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct acpi_memory_device { 843b74863df5d46f794052b5ee010cfc8fd66819ddPatrick Mochel struct acpi_device * device; 854be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown unsigned int state; /* State of the memory device */ 869ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct list_head res_list; 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 89887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Gotostatic int acpi_hotmem_initialized; 90887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto 919ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyukistatic acpi_status 929ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyukiacpi_memory_get_resource(struct acpi_resource *resource, void *context) 939ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki{ 949ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_memory_device *mem_device = context; 959ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_resource_address64 address64; 969ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_memory_info *info, *new; 979ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki acpi_status status; 989ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 999ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki status = acpi_resource_to_address64(resource, &address64); 1009ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (ACPI_FAILURE(status) || 1019ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki (address64.resource_type != ACPI_MEMORY_RANGE)) 1029ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return AE_OK; 1039ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 1049ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki list_for_each_entry(info, &mem_device->res_list, list) { 1059ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki /* Can we combine the resource range information? */ 1069ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if ((info->caching == address64.info.mem.caching) && 1079ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki (info->write_protect == address64.info.mem.write_protect) && 1089ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki (info->start_addr + info->length == address64.minimum)) { 1099ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki info->length += address64.address_length; 1109ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return AE_OK; 1119ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki } 1129ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki } 1139ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 1149ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL); 1159ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (!new) 1169ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return AE_ERROR; 1179ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 1189ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki INIT_LIST_HEAD(&new->list); 1199ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki new->caching = address64.info.mem.caching; 1209ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki new->write_protect = address64.info.mem.write_protect; 1219ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki new->start_addr = address64.minimum; 1229ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki new->length = address64.address_length; 1239ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki list_add_tail(&new->list, &mem_device->res_list); 1249ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 1259ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return AE_OK; 1269ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki} 1279ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsacpi_memory_get_device_resources(struct acpi_memory_device *mem_device) 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 1329ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_memory_info *info, *n; 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1355d2870faaa1fdcec795a6bf4dbbfc3e5d57fd7abKAMEZAWA Hiroyuki if (!list_empty(&mem_device->res_list)) 1365d2870faaa1fdcec795a6bf4dbbfc3e5d57fd7abKAMEZAWA Hiroyuki return 0; 1375d2870faaa1fdcec795a6bf4dbbfc3e5d57fd7abKAMEZAWA Hiroyuki 138b863278523f7adbacb9e34133f4b6397cdab9977Patrick Mochel status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS, 1399ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki acpi_memory_get_resource, mem_device); 1409ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (ACPI_FAILURE(status)) { 1419ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki list_for_each_entry_safe(info, n, &mem_device->res_list, list) 1429ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki kfree(info); 1435d2870faaa1fdcec795a6bf4dbbfc3e5d57fd7abKAMEZAWA Hiroyuki INIT_LIST_HEAD(&mem_device->res_list); 144d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 147d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return 0; 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsacpi_memory_get_device(acpi_handle handle, 1524be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown struct acpi_memory_device **mem_device) 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_handle phandle; 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_device *device = NULL; 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_device *pdevice = NULL; 158aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui int result; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acpi_bus_get_device(handle, &device) && device) 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto end; 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = acpi_get_parent(handle, &phandle); 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ACPI_FAILURE(status)) { 166a6fc67202e0224e6c9d1d285cc0b444bce887ed5Thomas Renninger ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent")); 167d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get the parent device */ 171aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui result = acpi_bus_get_device(phandle, &pdevice); 172aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui if (result) { 173aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui printk(KERN_WARNING PREFIX "Cannot get acpi bus device"); 174d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Now add the notified device. This creates the acpi_device 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and invokes .add function 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 181aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); 182aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui if (result) { 183aa7b2b2e973874df99a45b31adbed5978b46be1fZhao Yakui printk(KERN_WARNING PREFIX "Cannot add acpi bus"); 184d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1874be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown end: 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *mem_device = acpi_driver_data(device); 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!(*mem_device)) { 1904be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown printk(KERN_ERR "\n driver data not found"); 191d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 194d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return 0; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1974be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_check_device(struct acpi_memory_device *mem_device) 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 19927663c5855b10af9ec67bc7dfba001426ba21222Matthew Wilcox unsigned long long current_status; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get device present/absent information from the _STA */ 202b863278523f7adbacb9e34133f4b6397cdab9977Patrick Mochel if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA", 2034be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown NULL, ¤t_status))) 204d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check for device status. Device should be 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * present/enabled/functioning. 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 209a0bd4ac498acfe60f7533d15ba60d5efdd4e9ca5Bjorn Helgaas if (!((current_status & ACPI_STA_DEVICE_PRESENT) 210a0bd4ac498acfe60f7533d15ba60d5efdd4e9ca5Bjorn Helgaas && (current_status & ACPI_STA_DEVICE_ENABLED) 211a0bd4ac498acfe60f7533d15ba60d5efdd4e9ca5Bjorn Helgaas && (current_status & ACPI_STA_DEVICE_FUNCTIONING))) 212d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 214d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return 0; 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2174be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_enable_device(struct acpi_memory_device *mem_device) 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2199ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki int result, num_enabled = 0; 2209ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_memory_info *info; 2211e3590e2e4a38e8390fdac5bda23330bf2801838Yasunori Goto int node; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get the range from the _CRS */ 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds result = acpi_memory_get_device_resources(mem_device); 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (result) { 2276468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX "get_device_resources failed\n"); 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device->state = MEMORY_INVALID_STATE; 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return result; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 232b863278523f7adbacb9e34133f4b6397cdab9977Patrick Mochel node = acpi_get_node(mem_device->device->handle); 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Tell the VM there is more memory here... 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: Assume that this function returns zero on success 2369ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki * We don't have memory-hot-add rollback function,now. 2379ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki * (i.e. memory-hot-remove function) 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2399ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki list_for_each_entry(info, &mem_device->res_list, list) { 240fa25d8d6d3fa0fecd00cd4b909011291eae9257dKAMEZAWA Hiroyuki if (info->enabled) { /* just sanity check...*/ 241dd56a8e36f91f63c0a31e8a118d87b7cf01526b8Yasunori Goto num_enabled++; 242dd56a8e36f91f63c0a31e8a118d87b7cf01526b8Yasunori Goto continue; 243dd56a8e36f91f63c0a31e8a118d87b7cf01526b8Yasunori Goto } 2445d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui /* 2455d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * If the memory block size is zero, please ignore it. 2465d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * Don't try to do the following memory hotplug flowchart. 2475d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui */ 2485d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui if (!info->length) 2495d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui continue; 2508c2676a5870ab15cbeea9f826266bc946fe3cc26Keith Mannthey if (node < 0) 2518c2676a5870ab15cbeea9f826266bc946fe3cc26Keith Mannthey node = memory_add_physaddr_to_nid(info->start_addr); 2528c2676a5870ab15cbeea9f826266bc946fe3cc26Keith Mannthey 253bc02af93dd2bbddce1b55e0a493f833a1b7cf140Yasunori Goto result = add_memory(node, info->start_addr, info->length); 2549ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (result) 2559ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki continue; 2569ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki info->enabled = 1; 2579ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki num_enabled++; 2589ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki } 2599ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (!num_enabled) { 2600a1f1ab8de815cb63a48d24450f6b5fbb1b1f89cAndrew Morton printk(KERN_ERR PREFIX "add_memory failed\n"); 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device->state = MEMORY_INVALID_STATE; 2629ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return -EINVAL; 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2645d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui /* 2655d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * Sometimes the memory device will contain several memory blocks. 2665d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * When one memory block is hot-added to the system memory, it will 2675d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * be regarded as a success. 2685d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * Otherwise if the last memory block can't be hot-added to the system 2695d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * memory, it will be failure and the memory device can't be bound with 2705d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui * driver. 2715d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui */ 2725d2619fca753d270e63e76c9e18437b0d9bc8d75Zhao Yakui return 0; 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2754be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 2784be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown struct acpi_object_list arg_list; 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds union acpi_object arg; 28027663c5855b10af9ec67bc7dfba001426ba21222Matthew Wilcox unsigned long long current_status; 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Issue the _EJ0 command */ 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds arg_list.count = 1; 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds arg_list.pointer = &arg; 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds arg.type = ACPI_TYPE_INTEGER; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds arg.integer.value = 1; 288b863278523f7adbacb9e34133f4b6397cdab9977Patrick Mochel status = acpi_evaluate_object(mem_device->device->handle, 2894be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown "_EJ0", &arg_list, NULL); 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Return on _EJ0 failure */ 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ACPI_FAILURE(status)) { 292a6fc67202e0224e6c9d1d285cc0b444bce887ed5Thomas Renninger ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed")); 293d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Evalute _STA to check if the device is disabled */ 297b863278523f7adbacb9e34133f4b6397cdab9977Patrick Mochel status = acpi_evaluate_integer(mem_device->device->handle, "_STA", 2984be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown NULL, ¤t_status); 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ACPI_FAILURE(status)) 300d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check for device status. Device should be disabled */ 303a0bd4ac498acfe60f7533d15ba60d5efdd4e9ca5Bjorn Helgaas if (current_status & ACPI_STA_DEVICE_ENABLED) 304d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 306d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return 0; 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3094be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_disable_device(struct acpi_memory_device *mem_device) 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int result; 3129ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki struct acpi_memory_info *info, *n; 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Ask the VM to offline this memory range. 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Note: Assume that this function returns zero on success 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3199ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki list_for_each_entry_safe(info, n, &mem_device->res_list, list) { 3209ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (info->enabled) { 3219ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki result = remove_memory(info->start_addr, info->length); 3229ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki if (result) 3239ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki return result; 3249ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki } 3259ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki kfree(info); 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Power-off and eject the device */ 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds result = acpi_memory_powerdown_device(mem_device); 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (result) { 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set the status of the device to invalid */ 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device->state = MEMORY_INVALID_STATE; 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return result; 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device->state = MEMORY_POWER_OFF_STATE; 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return result; 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3404be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_memory_device *mem_device; 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_device *device; 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (event) { 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case ACPI_NOTIFY_BUS_CHECK: 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3494be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown "\nReceived BUS CHECK notification for device\n")); 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Fall Through */ 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case ACPI_NOTIFY_DEVICE_CHECK: 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (event == ACPI_NOTIFY_DEVICE_CHECK) 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3544be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown "\nReceived DEVICE CHECK notification for device\n")); 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acpi_memory_get_device(handle, &mem_device)) { 3566468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX "Cannot find driver data\n"); 357d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return; 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!acpi_memory_check_device(mem_device)) { 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acpi_memory_enable_device(mem_device)) 3626468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX 3636468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown "Cannot enable memory device\n"); 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case ACPI_NOTIFY_EJECT_REQUEST: 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3684be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown "\nReceived EJECT REQUEST notification for device\n")); 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acpi_bus_get_device(handle, &device)) { 3716468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX "Device doesn't exist\n"); 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device = acpi_driver_data(device); 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mem_device) { 3766468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX "Driver Data is NULL\n"); 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Currently disabling memory device from kernel mode 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TBD: Can also be disabled from user mode scripts 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TBD: Can also be disabled by Callback registration 3844be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown * with generic sysfs driver 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (acpi_memory_disable_device(mem_device)) 3876468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown printk(KERN_ERR PREFIX 3886468463abd7051fcc29f3ee7c931f9bbbb26f5a4Len Brown "Disable memory device\n"); 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * TBD: Invoke acpi_bus_remove to cleanup data structures 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3954be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown "Unsupported event [0x%x]\n", event)); 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 399d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return; 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4024be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int acpi_memory_device_add(struct acpi_device *device) 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int result; 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_memory_device *mem_device = NULL; 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!device) 409d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -EINVAL; 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 41136bcbec7ce21e2e8b3143b11a05747330abeca70Burman Yan mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL); 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mem_device) 413d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENOMEM; 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4159ac023989e6dd1b97140b47fb942a7940d0b2af2KAMEZAWA Hiroyuki INIT_LIST_HEAD(&mem_device->res_list); 4163b74863df5d46f794052b5ee010cfc8fd66819ddPatrick Mochel mem_device->device = device; 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); 419db89b4f0dbab837d0f3de2c3e9427a8d5393afa3Pavel Machek device->driver_data = mem_device; 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get the range from the _CRS */ 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds result = acpi_memory_get_device_resources(mem_device); 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (result) { 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mem_device); 425d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return result; 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set the device state */ 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mem_device->state = MEMORY_POWER_ON_STATE; 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4316cbe44cd8d48a92856295f445183f52bf42a544dYasunori Goto printk(KERN_DEBUG "%s \n", acpi_device_name(device)); 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 433887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto /* 434887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto * Early boot code has recognized memory area by EFI/E820. 435887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto * If DSDT shows these memory devices on boot, hotplug is not necessary 436887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto * for them. So, it just returns until completion of this driver's 437887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto * start up. 438887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto */ 439887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto if (!acpi_hotmem_initialized) 440887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto return 0; 441887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto 4421f425994f96d85540d47eee98daabc1e211b454eYasunori Goto if (!acpi_memory_check_device(mem_device)) { 4431f425994f96d85540d47eee98daabc1e211b454eYasunori Goto /* call add_memory func */ 4441f425994f96d85540d47eee98daabc1e211b454eYasunori Goto result = acpi_memory_enable_device(mem_device); 4451f425994f96d85540d47eee98daabc1e211b454eYasunori Goto if (result) 44655ac9a018f83e4f42f3c6ce98a8dbda73b985935Lin Ming printk(KERN_ERR PREFIX 44755ac9a018f83e4f42f3c6ce98a8dbda73b985935Lin Ming "Error in acpi_memory_enable_device\n"); 4481f425994f96d85540d47eee98daabc1e211b454eYasunori Goto } 449d120cfb544ed6161b9d32fb6c4648c471807ee6bLen Brown return result; 4501f425994f96d85540d47eee98daabc1e211b454eYasunori Goto} 4511f425994f96d85540d47eee98daabc1e211b454eYasunori Goto 45280f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaasstatic int acpi_memory_device_remove(struct acpi_device *device, int type) 45380f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas{ 45480f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas struct acpi_memory_device *mem_device = NULL; 45580f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas 45680f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas 45780f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas if (!device || !acpi_driver_data(device)) 45880f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas return -EINVAL; 45980f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas 46080f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas mem_device = acpi_driver_data(device); 46180f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas kfree(mem_device); 46280f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas 46380f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas return 0; 46480f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas} 46580f20fef6a2381402e59b169eb51b989cc175ab7Bjorn Helgaas 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Helper function to check for memory device 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4694be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic acpi_status is_memory_device(acpi_handle handle) 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char *hardware_id; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct acpi_device_info *info; 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 47515b8dd53f5ffaf8e2d9095c423f713423f576c0fBob Moore status = acpi_get_object_info(handle, &info); 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ACPI_FAILURE(status)) 477d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return status; 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!(info->valid & ACPI_VALID_HID)) { 48015b8dd53f5ffaf8e2d9095c423f713423f576c0fBob Moore kfree(info); 481d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return AE_ERROR; 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 48415b8dd53f5ffaf8e2d9095c423f713423f576c0fBob Moore hardware_id = info->hardware_id.string; 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((hardware_id == NULL) || 4864be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = AE_ERROR; 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 48915b8dd53f5ffaf8e2d9095c423f713423f576c0fBob Moore kfree(info); 490d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return status; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic acpi_status 4944be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownacpi_memory_register_notify_handler(acpi_handle handle, 4954be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown u32 level, void *ctxt, void **retv) 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = is_memory_device(handle); 50107dd4855e7fffeb50565826e5e736509ee8f6129Yasunori Goto if (ACPI_FAILURE(status)) 502d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return AE_OK; /* continue */ 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, 5054be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown acpi_memory_device_notify, NULL); 506a6fc67202e0224e6c9d1d285cc0b444bce887ed5Thomas Renninger /* continue */ 507d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return AE_OK; 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic acpi_status 5114be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownacpi_memory_deregister_notify_handler(acpi_handle handle, 5124be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown u32 level, void *ctxt, void **retv) 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = is_memory_device(handle); 51807dd4855e7fffeb50565826e5e736509ee8f6129Yasunori Goto if (ACPI_FAILURE(status)) 519d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return AE_OK; /* continue */ 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = acpi_remove_notify_handler(handle, 5224be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown ACPI_SYSTEM_NOTIFY, 5234be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown acpi_memory_device_notify); 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 525d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return AE_OK; /* continue */ 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5284be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic int __init acpi_memory_device_init(void) 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int result; 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds result = acpi_bus_register_driver(&acpi_memory_device_driver); 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (result < 0) 537d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 5404be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown ACPI_UINT32_MAX, 5412263576cfc6e8f6ab038126c3254404b9fcb1c33Lin Ming acpi_memory_register_notify_handler, NULL, 5424be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown NULL, NULL); 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5444be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown if (ACPI_FAILURE(status)) { 545a6fc67202e0224e6c9d1d285cc0b444bce887ed5Thomas Renninger ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_bus_unregister_driver(&acpi_memory_device_driver); 547d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return -ENODEV; 5484be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown } 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 550887b95931b4072e60e3bf4253ff7bffe372bca46Yasunori Goto acpi_hotmem_initialized = 1; 551d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return 0; 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5544be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brownstatic void __exit acpi_memory_device_exit(void) 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_status status; 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Adding this to un-install notification handlers for all the device 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * handles. 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 5644be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown ACPI_UINT32_MAX, 5652263576cfc6e8f6ab038126c3254404b9fcb1c33Lin Ming acpi_memory_deregister_notify_handler, NULL, 5664be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown NULL, NULL); 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5684be44fcd3bf648b782f4460fd06dfae6c42ded4bLen Brown if (ACPI_FAILURE(status)) 569a6fc67202e0224e6c9d1d285cc0b444bce887ed5Thomas Renninger ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds acpi_bus_unregister_driver(&acpi_memory_device_driver); 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 573d550d98d3317378d93a4869db204725d270ec812Patrick Mochel return; 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(acpi_memory_device_init); 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(acpi_memory_device_exit); 578