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, &current_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, &current_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