xref: /linux/drivers/acpi/acpi_memhotplug.c (revision e3c2bfdd)
14359375cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
30a347644SRafael J. Wysocki  * Copyright (C) 2004, 2013 Intel Corporation
40a347644SRafael J. Wysocki  * Author: Naveen B S <naveen.b.s@intel.com>
50a347644SRafael J. Wysocki  * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * All rights reserved.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * ACPI based HotPlug driver that supports Memory Hotplug
10c7060d9eSNick Andrew  * This driver fields notifications from firmware for memory add
111da177e4SLinus Torvalds  * and remove operations and alerts the VM of the affected memory
121da177e4SLinus Torvalds  * ranges.
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
15ab6c5709SToshi Kani #include <linux/acpi.h>
16e2ff3940SRafael J. Wysocki #include <linux/memory.h>
170a347644SRafael J. Wysocki #include <linux/memory_hotplug.h>
180a347644SRafael J. Wysocki 
190a347644SRafael J. Wysocki #include "internal.h"
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #define ACPI_MEMORY_DEVICE_CLASS		"memory"
221da177e4SLinus Torvalds #define ACPI_MEMORY_DEVICE_HID			"PNP0C80"
231da177e4SLinus Torvalds #define ACPI_MEMORY_DEVICE_NAME			"Hotplug Mem Device"
241da177e4SLinus Torvalds 
25cccd4208SRafael J. Wysocki static const struct acpi_device_id memory_device_ids[] = {
26cccd4208SRafael J. Wysocki 	{ACPI_MEMORY_DEVICE_HID, 0},
27cccd4208SRafael J. Wysocki 	{"", 0},
28cccd4208SRafael J. Wysocki };
29cccd4208SRafael J. Wysocki 
30cccd4208SRafael J. Wysocki #ifdef CONFIG_ACPI_HOTPLUG_MEMORY
31cccd4208SRafael J. Wysocki 
320a347644SRafael J. Wysocki static int acpi_memory_device_add(struct acpi_device *device,
330a347644SRafael J. Wysocki 				  const struct acpi_device_id *not_used);
340a347644SRafael J. Wysocki static void acpi_memory_device_remove(struct acpi_device *device);
351da177e4SLinus Torvalds 
360a347644SRafael J. Wysocki static struct acpi_scan_handler memory_device_handler = {
371ba90e3aSThomas Renninger 	.ids = memory_device_ids,
380a347644SRafael J. Wysocki 	.attach = acpi_memory_device_add,
390a347644SRafael J. Wysocki 	.detach = acpi_memory_device_remove,
400a347644SRafael J. Wysocki 	.hotplug = {
410a347644SRafael J. Wysocki 		.enabled = true,
421da177e4SLinus Torvalds 	},
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds 
459ac02398SKAMEZAWA Hiroyuki struct acpi_memory_info {
469ac02398SKAMEZAWA Hiroyuki 	struct list_head list;
479ac02398SKAMEZAWA Hiroyuki 	u64 start_addr;		/* Memory Range start physical addr */
489ac02398SKAMEZAWA Hiroyuki 	u64 length;		/* Memory Range length */
499ac02398SKAMEZAWA Hiroyuki 	unsigned short caching;	/* memory cache attribute */
509ac02398SKAMEZAWA Hiroyuki 	unsigned short write_protect;	/* memory read/write attribute */
519ac02398SKAMEZAWA Hiroyuki 	unsigned int enabled:1;
529ac02398SKAMEZAWA Hiroyuki };
539ac02398SKAMEZAWA Hiroyuki 
541da177e4SLinus Torvalds struct acpi_memory_device {
553b74863dSPatrick Mochel 	struct acpi_device *device;
569ac02398SKAMEZAWA Hiroyuki 	struct list_head res_list;
57*2a157839SDavid Hildenbrand 	int mgid;
581da177e4SLinus Torvalds };
591da177e4SLinus Torvalds 
609ac02398SKAMEZAWA Hiroyuki static acpi_status
acpi_memory_get_resource(struct acpi_resource * resource,void * context)619ac02398SKAMEZAWA Hiroyuki acpi_memory_get_resource(struct acpi_resource *resource, void *context)
629ac02398SKAMEZAWA Hiroyuki {
639ac02398SKAMEZAWA Hiroyuki 	struct acpi_memory_device *mem_device = context;
649ac02398SKAMEZAWA Hiroyuki 	struct acpi_resource_address64 address64;
659ac02398SKAMEZAWA Hiroyuki 	struct acpi_memory_info *info, *new;
669ac02398SKAMEZAWA Hiroyuki 	acpi_status status;
679ac02398SKAMEZAWA Hiroyuki 
689ac02398SKAMEZAWA Hiroyuki 	status = acpi_resource_to_address64(resource, &address64);
699ac02398SKAMEZAWA Hiroyuki 	if (ACPI_FAILURE(status) ||
709ac02398SKAMEZAWA Hiroyuki 	    (address64.resource_type != ACPI_MEMORY_RANGE))
719ac02398SKAMEZAWA Hiroyuki 		return AE_OK;
729ac02398SKAMEZAWA Hiroyuki 
739ac02398SKAMEZAWA Hiroyuki 	list_for_each_entry(info, &mem_device->res_list, list) {
749ac02398SKAMEZAWA Hiroyuki 		/* Can we combine the resource range information? */
759ac02398SKAMEZAWA Hiroyuki 		if ((info->caching == address64.info.mem.caching) &&
769ac02398SKAMEZAWA Hiroyuki 		    (info->write_protect == address64.info.mem.write_protect) &&
77a45de93eSLv Zheng 		    (info->start_addr + info->length == address64.address.minimum)) {
78a45de93eSLv Zheng 			info->length += address64.address.address_length;
799ac02398SKAMEZAWA Hiroyuki 			return AE_OK;
809ac02398SKAMEZAWA Hiroyuki 		}
819ac02398SKAMEZAWA Hiroyuki 	}
829ac02398SKAMEZAWA Hiroyuki 
839ac02398SKAMEZAWA Hiroyuki 	new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
849ac02398SKAMEZAWA Hiroyuki 	if (!new)
859ac02398SKAMEZAWA Hiroyuki 		return AE_ERROR;
869ac02398SKAMEZAWA Hiroyuki 
879ac02398SKAMEZAWA Hiroyuki 	INIT_LIST_HEAD(&new->list);
889ac02398SKAMEZAWA Hiroyuki 	new->caching = address64.info.mem.caching;
899ac02398SKAMEZAWA Hiroyuki 	new->write_protect = address64.info.mem.write_protect;
90a45de93eSLv Zheng 	new->start_addr = address64.address.minimum;
91a45de93eSLv Zheng 	new->length = address64.address.address_length;
929ac02398SKAMEZAWA Hiroyuki 	list_add_tail(&new->list, &mem_device->res_list);
939ac02398SKAMEZAWA Hiroyuki 
949ac02398SKAMEZAWA Hiroyuki 	return AE_OK;
959ac02398SKAMEZAWA Hiroyuki }
969ac02398SKAMEZAWA Hiroyuki 
97386e52b9SWen Congyang static void
acpi_memory_free_device_resources(struct acpi_memory_device * mem_device)98386e52b9SWen Congyang acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
99386e52b9SWen Congyang {
100386e52b9SWen Congyang 	struct acpi_memory_info *info, *n;
101386e52b9SWen Congyang 
102386e52b9SWen Congyang 	list_for_each_entry_safe(info, n, &mem_device->res_list, list)
103386e52b9SWen Congyang 		kfree(info);
104386e52b9SWen Congyang 	INIT_LIST_HEAD(&mem_device->res_list);
105386e52b9SWen Congyang }
106386e52b9SWen Congyang 
1071da177e4SLinus Torvalds static int
acpi_memory_get_device_resources(struct acpi_memory_device * mem_device)1081da177e4SLinus Torvalds acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
1091da177e4SLinus Torvalds {
1101da177e4SLinus Torvalds 	acpi_status status;
1111da177e4SLinus Torvalds 
1125d2870faSKAMEZAWA Hiroyuki 	if (!list_empty(&mem_device->res_list))
1135d2870faSKAMEZAWA Hiroyuki 		return 0;
1145d2870faSKAMEZAWA Hiroyuki 
115b8632785SPatrick Mochel 	status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
1169ac02398SKAMEZAWA Hiroyuki 				     acpi_memory_get_resource, mem_device);
1179ac02398SKAMEZAWA Hiroyuki 	if (ACPI_FAILURE(status)) {
118386e52b9SWen Congyang 		acpi_memory_free_device_resources(mem_device);
119d550d98dSPatrick Mochel 		return -EINVAL;
1201da177e4SLinus Torvalds 	}
1211da177e4SLinus Torvalds 
122d550d98dSPatrick Mochel 	return 0;
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
acpi_memory_check_device(struct acpi_memory_device * mem_device)1254be44fcdSLen Brown static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
1261da177e4SLinus Torvalds {
12727663c58SMatthew Wilcox 	unsigned long long current_status;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	/* Get device present/absent information from the _STA */
13016ff816dSZhang Yanfei 	if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
13116ff816dSZhang Yanfei 					       METHOD_NAME__STA, NULL,
13216ff816dSZhang Yanfei 					       &current_status)))
133d550d98dSPatrick Mochel 		return -ENODEV;
1341da177e4SLinus Torvalds 	/*
1351da177e4SLinus Torvalds 	 * Check for device status. Device should be
1361da177e4SLinus Torvalds 	 * present/enabled/functioning.
1371da177e4SLinus Torvalds 	 */
138a0bd4ac4SBjorn Helgaas 	if (!((current_status & ACPI_STA_DEVICE_PRESENT)
139a0bd4ac4SBjorn Helgaas 	      && (current_status & ACPI_STA_DEVICE_ENABLED)
140a0bd4ac4SBjorn Helgaas 	      && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
141d550d98dSPatrick Mochel 		return -ENODEV;
1421da177e4SLinus Torvalds 
143d550d98dSPatrick Mochel 	return 0;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds 
acpi_bind_memblk(struct memory_block * mem,void * arg)146e2ff3940SRafael J. Wysocki static int acpi_bind_memblk(struct memory_block *mem, void *arg)
147e2ff3940SRafael J. Wysocki {
14824dee1fcSRafael J. Wysocki 	return acpi_bind_one(&mem->dev, arg);
149e2ff3940SRafael J. Wysocki }
150e2ff3940SRafael J. Wysocki 
acpi_bind_memory_blocks(struct acpi_memory_info * info,struct acpi_device * adev)151e2ff3940SRafael J. Wysocki static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
15224dee1fcSRafael J. Wysocki 				   struct acpi_device *adev)
153e2ff3940SRafael J. Wysocki {
154fbcf73ceSDavid Hildenbrand 	return walk_memory_blocks(info->start_addr, info->length, adev,
155e2ff3940SRafael J. Wysocki 				  acpi_bind_memblk);
156e2ff3940SRafael J. Wysocki }
157e2ff3940SRafael J. Wysocki 
acpi_unbind_memblk(struct memory_block * mem,void * arg)158e2ff3940SRafael J. Wysocki static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
159e2ff3940SRafael J. Wysocki {
160e2ff3940SRafael J. Wysocki 	acpi_unbind_one(&mem->dev);
161e2ff3940SRafael J. Wysocki 	return 0;
162e2ff3940SRafael J. Wysocki }
163e2ff3940SRafael J. Wysocki 
acpi_unbind_memory_blocks(struct acpi_memory_info * info)16424dee1fcSRafael J. Wysocki static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)
165e2ff3940SRafael J. Wysocki {
166fbcf73ceSDavid Hildenbrand 	walk_memory_blocks(info->start_addr, info->length, NULL,
167fbcf73ceSDavid Hildenbrand 			   acpi_unbind_memblk);
168e2ff3940SRafael J. Wysocki }
169e2ff3940SRafael J. Wysocki 
acpi_memory_enable_device(struct acpi_memory_device * mem_device)1704be44fcdSLen Brown static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
1711da177e4SLinus Torvalds {
172e2ff3940SRafael J. Wysocki 	acpi_handle handle = mem_device->device->handle;
173*2a157839SDavid Hildenbrand 	mhp_t mhp_flags = MHP_NID_IS_MGID;
1749ac02398SKAMEZAWA Hiroyuki 	int result, num_enabled = 0;
1759ac02398SKAMEZAWA Hiroyuki 	struct acpi_memory_info *info;
176*2a157839SDavid Hildenbrand 	u64 total_length = 0;
177*2a157839SDavid Hildenbrand 	int node, mgid;
1781da177e4SLinus Torvalds 
179e2ff3940SRafael J. Wysocki 	node = acpi_get_node(handle);
180*2a157839SDavid Hildenbrand 
181*2a157839SDavid Hildenbrand 	list_for_each_entry(info, &mem_device->res_list, list) {
182*2a157839SDavid Hildenbrand 		if (!info->length)
183*2a157839SDavid Hildenbrand 			continue;
184*2a157839SDavid Hildenbrand 		/* We want a single node for the whole memory group */
185*2a157839SDavid Hildenbrand 		if (node < 0)
186*2a157839SDavid Hildenbrand 			node = memory_add_physaddr_to_nid(info->start_addr);
187*2a157839SDavid Hildenbrand 		total_length += info->length;
188*2a157839SDavid Hildenbrand 	}
189*2a157839SDavid Hildenbrand 
190*2a157839SDavid Hildenbrand 	if (!total_length) {
191*2a157839SDavid Hildenbrand 		dev_err(&mem_device->device->dev, "device is empty\n");
192*2a157839SDavid Hildenbrand 		return -EINVAL;
193*2a157839SDavid Hildenbrand 	}
194*2a157839SDavid Hildenbrand 
195*2a157839SDavid Hildenbrand 	mgid = memory_group_register_static(node, PFN_UP(total_length));
196*2a157839SDavid Hildenbrand 	if (mgid < 0)
197*2a157839SDavid Hildenbrand 		return mgid;
198*2a157839SDavid Hildenbrand 	mem_device->mgid = mgid;
199*2a157839SDavid Hildenbrand 
2001da177e4SLinus Torvalds 	/*
2011da177e4SLinus Torvalds 	 * Tell the VM there is more memory here...
2021da177e4SLinus Torvalds 	 * Note: Assume that this function returns zero on success
2039ac02398SKAMEZAWA Hiroyuki 	 * We don't have memory-hot-add rollback function,now.
2049ac02398SKAMEZAWA Hiroyuki 	 * (i.e. memory-hot-remove function)
2051da177e4SLinus Torvalds 	 */
2069ac02398SKAMEZAWA Hiroyuki 	list_for_each_entry(info, &mem_device->res_list, list) {
2075d2619fcSZhao Yakui 		/*
2085d2619fcSZhao Yakui 		 * If the memory block size is zero, please ignore it.
2095d2619fcSZhao Yakui 		 * Don't try to do the following memory hotplug flowchart.
2105d2619fcSZhao Yakui 		 */
2115d2619fcSZhao Yakui 		if (!info->length)
2125d2619fcSZhao Yakui 			continue;
2138c2676a5SKeith Mannthey 
2144a3e5de9SOscar Salvador 		mhp_flags |= MHP_MEMMAP_ON_MEMORY;
215*2a157839SDavid Hildenbrand 		result = __add_memory(mgid, info->start_addr, info->length,
2164a3e5de9SOscar Salvador 				      mhp_flags);
21765479472SWen Congyang 
21865479472SWen Congyang 		/*
21965479472SWen Congyang 		 * If the memory block has been used by the kernel, add_memory()
22065479472SWen Congyang 		 * returns -EEXIST. If add_memory() returns the other error, it
22165479472SWen Congyang 		 * means that this memory block is not used by the kernel.
22265479472SWen Congyang 		 */
223fd4655c2SYasuaki Ishimatsu 		if (result && result != -EEXIST)
2249ac02398SKAMEZAWA Hiroyuki 			continue;
22565479472SWen Congyang 
22624dee1fcSRafael J. Wysocki 		result = acpi_bind_memory_blocks(info, mem_device->device);
227e2ff3940SRafael J. Wysocki 		if (result) {
22824dee1fcSRafael J. Wysocki 			acpi_unbind_memory_blocks(info);
229e2ff3940SRafael J. Wysocki 			return -ENODEV;
230e2ff3940SRafael J. Wysocki 		}
231e2ff3940SRafael J. Wysocki 
2329ac02398SKAMEZAWA Hiroyuki 		info->enabled = 1;
233bb49d82dSYasuaki Ishimatsu 
23465479472SWen Congyang 		/*
23565479472SWen Congyang 		 * Add num_enable even if add_memory() returns -EEXIST, so the
23665479472SWen Congyang 		 * device is bound to this driver.
23765479472SWen Congyang 		 */
2389ac02398SKAMEZAWA Hiroyuki 		num_enabled++;
2399ac02398SKAMEZAWA Hiroyuki 	}
2409ac02398SKAMEZAWA Hiroyuki 	if (!num_enabled) {
241ab6c5709SToshi Kani 		dev_err(&mem_device->device->dev, "add_memory failed\n");
2429ac02398SKAMEZAWA Hiroyuki 		return -EINVAL;
2431da177e4SLinus Torvalds 	}
2445d2619fcSZhao Yakui 	/*
2455d2619fcSZhao Yakui 	 * Sometimes the memory device will contain several memory blocks.
2465d2619fcSZhao Yakui 	 * When one memory block is hot-added to the system memory, it will
2475d2619fcSZhao Yakui 	 * be regarded as a success.
2485d2619fcSZhao Yakui 	 * Otherwise if the last memory block can't be hot-added to the system
2495d2619fcSZhao Yakui 	 * memory, it will be failure and the memory device can't be bound with
2505d2619fcSZhao Yakui 	 * driver.
2515d2619fcSZhao Yakui 	 */
2525d2619fcSZhao Yakui 	return 0;
2531da177e4SLinus Torvalds }
2541da177e4SLinus Torvalds 
acpi_memory_remove_memory(struct acpi_memory_device * mem_device)255242831ebSRafael J. Wysocki static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
2561da177e4SLinus Torvalds {
2579ac02398SKAMEZAWA Hiroyuki 	struct acpi_memory_info *info, *n;
25860a5a19eSTang Chen 
2599ac02398SKAMEZAWA Hiroyuki 	list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
26065479472SWen Congyang 		if (!info->enabled)
261fd4655c2SYasuaki Ishimatsu 			continue;
26265479472SWen Congyang 
26324dee1fcSRafael J. Wysocki 		acpi_unbind_memory_blocks(info);
264e1c158e4SDavid Hildenbrand 		__remove_memory(info->start_addr, info->length);
26519387b27SYasuaki Ishimatsu 		list_del(&info->list);
2669ac02398SKAMEZAWA Hiroyuki 		kfree(info);
2671da177e4SLinus Torvalds 	}
26819387b27SYasuaki Ishimatsu }
26919387b27SYasuaki Ishimatsu 
acpi_memory_device_free(struct acpi_memory_device * mem_device)270386e52b9SWen Congyang static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
271386e52b9SWen Congyang {
272386e52b9SWen Congyang 	if (!mem_device)
273386e52b9SWen Congyang 		return;
274386e52b9SWen Congyang 
275*2a157839SDavid Hildenbrand 	/* In case we succeeded adding *some* memory, unregistering fails. */
276*2a157839SDavid Hildenbrand 	if (mem_device->mgid >= 0)
277*2a157839SDavid Hildenbrand 		memory_group_unregister(mem_device->mgid);
278*2a157839SDavid Hildenbrand 
279386e52b9SWen Congyang 	acpi_memory_free_device_resources(mem_device);
2800a347644SRafael J. Wysocki 	mem_device->device->driver_data = NULL;
281386e52b9SWen Congyang 	kfree(mem_device);
282386e52b9SWen Congyang }
283386e52b9SWen Congyang 
acpi_memory_device_add(struct acpi_device * device,const struct acpi_device_id * not_used)2840a347644SRafael J. Wysocki static int acpi_memory_device_add(struct acpi_device *device,
2850a347644SRafael J. Wysocki 				  const struct acpi_device_id *not_used)
2861da177e4SLinus Torvalds {
2870a347644SRafael J. Wysocki 	struct acpi_memory_device *mem_device;
2881da177e4SLinus Torvalds 	int result;
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	if (!device)
291d550d98dSPatrick Mochel 		return -EINVAL;
2921da177e4SLinus Torvalds 
29336bcbec7SBurman Yan 	mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
2941da177e4SLinus Torvalds 	if (!mem_device)
295d550d98dSPatrick Mochel 		return -ENOMEM;
2961da177e4SLinus Torvalds 
2979ac02398SKAMEZAWA Hiroyuki 	INIT_LIST_HEAD(&mem_device->res_list);
2983b74863dSPatrick Mochel 	mem_device->device = device;
299*2a157839SDavid Hildenbrand 	mem_device->mgid = -1;
3001da177e4SLinus Torvalds 	sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
3011da177e4SLinus Torvalds 	sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
302db89b4f0SPavel Machek 	device->driver_data = mem_device;
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	/* Get the range from the _CRS */
3051da177e4SLinus Torvalds 	result = acpi_memory_get_device_resources(mem_device);
3061da177e4SLinus Torvalds 	if (result) {
307d19f503eSToshi Kani 		device->driver_data = NULL;
3081da177e4SLinus Torvalds 		kfree(mem_device);
309d550d98dSPatrick Mochel 		return result;
3101da177e4SLinus Torvalds 	}
3111da177e4SLinus Torvalds 
3120a347644SRafael J. Wysocki 	result = acpi_memory_check_device(mem_device);
3130a347644SRafael J. Wysocki 	if (result) {
3140a347644SRafael J. Wysocki 		acpi_memory_device_free(mem_device);
3150a347644SRafael J. Wysocki 		return 0;
3160a347644SRafael J. Wysocki 	}
3171da177e4SLinus Torvalds 
31880f20fefSBjorn Helgaas 	result = acpi_memory_enable_device(mem_device);
319e0b7b24dSWen Congyang 	if (result) {
3200a347644SRafael J. Wysocki 		dev_err(&device->dev, "acpi_memory_enable_device() error\n");
321e0b7b24dSWen Congyang 		acpi_memory_device_free(mem_device);
322e2ff3940SRafael J. Wysocki 		return result;
3231da177e4SLinus Torvalds 	}
3241da177e4SLinus Torvalds 
3250a347644SRafael J. Wysocki 	dev_dbg(&device->dev, "Memory device configured by ACPI\n");
3260a347644SRafael J. Wysocki 	return 1;
3270a347644SRafael J. Wysocki }
3280a347644SRafael J. Wysocki 
acpi_memory_device_remove(struct acpi_device * device)3290a347644SRafael J. Wysocki static void acpi_memory_device_remove(struct acpi_device *device)
3301da177e4SLinus Torvalds {
3310a347644SRafael J. Wysocki 	struct acpi_memory_device *mem_device;
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	if (!device || !acpi_driver_data(device))
3340a347644SRafael J. Wysocki 		return;
3351da177e4SLinus Torvalds 
33650dd0969SJan Engelhardt 	mem_device = acpi_driver_data(device);
3370a347644SRafael J. Wysocki 	acpi_memory_remove_memory(mem_device);
338386e52b9SWen Congyang 	acpi_memory_device_free(mem_device);
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds 
34100159a20SPrarit Bhargava static bool __initdata acpi_no_memhotplug;
34200159a20SPrarit Bhargava 
acpi_memory_hotplug_init(void)3430a347644SRafael J. Wysocki void __init acpi_memory_hotplug_init(void)
3441da177e4SLinus Torvalds {
345cccd4208SRafael J. Wysocki 	if (acpi_no_memhotplug) {
346cccd4208SRafael J. Wysocki 		memory_device_handler.attach = NULL;
347cccd4208SRafael J. Wysocki 		acpi_scan_add_handler(&memory_device_handler);
34800159a20SPrarit Bhargava 		return;
349cccd4208SRafael J. Wysocki 	}
3500a347644SRafael J. Wysocki 	acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
3511da177e4SLinus Torvalds }
35200159a20SPrarit Bhargava 
disable_acpi_memory_hotplug(char * str)35300159a20SPrarit Bhargava static int __init disable_acpi_memory_hotplug(char *str)
35400159a20SPrarit Bhargava {
35500159a20SPrarit Bhargava 	acpi_no_memhotplug = true;
35600159a20SPrarit Bhargava 	return 1;
35700159a20SPrarit Bhargava }
35800159a20SPrarit Bhargava __setup("acpi_no_memhotplug", disable_acpi_memory_hotplug);
359cccd4208SRafael J. Wysocki 
360cccd4208SRafael J. Wysocki #else
361cccd4208SRafael J. Wysocki 
362cccd4208SRafael J. Wysocki static struct acpi_scan_handler memory_device_handler = {
363cccd4208SRafael J. Wysocki 	.ids = memory_device_ids,
364cccd4208SRafael J. Wysocki };
365cccd4208SRafael J. Wysocki 
acpi_memory_hotplug_init(void)366cccd4208SRafael J. Wysocki void __init acpi_memory_hotplug_init(void)
367cccd4208SRafael J. Wysocki {
368cccd4208SRafael J. Wysocki 	acpi_scan_add_handler(&memory_device_handler);
369cccd4208SRafael J. Wysocki }
370cccd4208SRafael J. Wysocki 
371cccd4208SRafael J. Wysocki #endif /* CONFIG_ACPI_HOTPLUG_MEMORY */
372