xref: /openbmc/linux/drivers/acpi/acpi_memhotplug.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1 /*
2  * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
3  *
4  * All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14  * NON INFRINGEMENT.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  *
22  * ACPI based HotPlug driver that supports Memory Hotplug
23  * This driver fields notifications from firmare for memory add
24  * and remove operations and alerts the VM of the affected memory
25  * ranges.
26  */
27 
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/types.h>
32 #include <linux/memory_hotplug.h>
33 #include <acpi/acpi_drivers.h>
34 
35 
36 #define ACPI_MEMORY_DEVICE_COMPONENT		0x08000000UL
37 #define ACPI_MEMORY_DEVICE_CLASS		"memory"
38 #define ACPI_MEMORY_DEVICE_HID			"PNP0C80"
39 #define ACPI_MEMORY_DEVICE_DRIVER_NAME		"Hotplug Mem Driver"
40 #define ACPI_MEMORY_DEVICE_NAME			"Hotplug Mem Device"
41 
42 #define _COMPONENT		ACPI_MEMORY_DEVICE_COMPONENT
43 
44 ACPI_MODULE_NAME		("acpi_memory")
45 MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
46 MODULE_DESCRIPTION(ACPI_MEMORY_DEVICE_DRIVER_NAME);
47 MODULE_LICENSE("GPL");
48 
49 /* ACPI _STA method values */
50 #define ACPI_MEMORY_STA_PRESENT		(0x00000001UL)
51 #define ACPI_MEMORY_STA_ENABLED		(0x00000002UL)
52 #define ACPI_MEMORY_STA_FUNCTIONAL	(0x00000008UL)
53 
54 /* Memory Device States */
55 #define MEMORY_INVALID_STATE	0
56 #define MEMORY_POWER_ON_STATE	1
57 #define MEMORY_POWER_OFF_STATE	2
58 
59 static int acpi_memory_device_add (struct acpi_device *device);
60 static int acpi_memory_device_remove (struct acpi_device *device, int type);
61 
62 static struct acpi_driver acpi_memory_device_driver = {
63 	.name =		ACPI_MEMORY_DEVICE_DRIVER_NAME,
64 	.class =	ACPI_MEMORY_DEVICE_CLASS,
65 	.ids =		ACPI_MEMORY_DEVICE_HID,
66 	.ops =		{
67 				.add =		acpi_memory_device_add,
68 				.remove =	acpi_memory_device_remove,
69 			},
70 };
71 
72 struct acpi_memory_device {
73 	acpi_handle handle;
74 	unsigned int state;		/* State of the memory device */
75 	unsigned short cache_attribute;	/* memory cache attribute */
76 	unsigned short read_write_attribute;/* memory read/write attribute */
77 	u64 start_addr;	/* Memory Range start physical addr */
78 	u64 end_addr;	/* Memory Range end physical addr */
79 };
80 
81 
82 static int
83 acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
84 {
85 	acpi_status status;
86 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
87 	struct acpi_resource *resource = NULL;
88 	struct acpi_resource_address64 address64;
89 
90 	ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources");
91 
92 	/* Get the range from the _CRS */
93 	status = acpi_get_current_resources(mem_device->handle, &buffer);
94 	if (ACPI_FAILURE(status))
95 		return_VALUE(-EINVAL);
96 
97 	resource = (struct acpi_resource *) buffer.pointer;
98 	status = acpi_resource_to_address64(resource, &address64);
99 	if (ACPI_SUCCESS(status)) {
100 		if (address64.resource_type == ACPI_MEMORY_RANGE) {
101 			/* Populate the structure */
102 			mem_device->cache_attribute =
103 				address64.attribute.memory.cache_attribute;
104 			mem_device->read_write_attribute =
105 			address64.attribute.memory.read_write_attribute;
106 			mem_device->start_addr = address64.min_address_range;
107 			mem_device->end_addr = address64.max_address_range;
108 		}
109 	}
110 
111 	acpi_os_free(buffer.pointer);
112 	return_VALUE(0);
113 }
114 
115 static int
116 acpi_memory_get_device(acpi_handle handle,
117 	struct acpi_memory_device **mem_device)
118 {
119 	acpi_status status;
120 	acpi_handle phandle;
121 	struct acpi_device *device = NULL;
122 	struct acpi_device *pdevice = NULL;
123 
124 	ACPI_FUNCTION_TRACE("acpi_memory_get_device");
125 
126 	if (!acpi_bus_get_device(handle, &device) && device)
127 		goto end;
128 
129 	status = acpi_get_parent(handle, &phandle);
130 	if (ACPI_FAILURE(status)) {
131 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
132 			"Error in acpi_get_parent\n"));
133 		return_VALUE(-EINVAL);
134 	}
135 
136 	/* Get the parent device */
137 	status = acpi_bus_get_device(phandle, &pdevice);
138 	if (ACPI_FAILURE(status)) {
139 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
140 			"Error in acpi_bus_get_device\n"));
141 		return_VALUE(-EINVAL);
142 	}
143 
144 	/*
145 	 * Now add the notified device.  This creates the acpi_device
146 	 * and invokes .add function
147 	 */
148 	status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
149 	if (ACPI_FAILURE(status)) {
150 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
151 			"Error in acpi_bus_add\n"));
152 		return_VALUE(-EINVAL);
153 	}
154 
155 end:
156 	*mem_device = acpi_driver_data(device);
157 	if (!(*mem_device)) {
158 		printk(KERN_ERR "\n driver data not found" );
159 		return_VALUE(-ENODEV);
160 	}
161 
162 	return_VALUE(0);
163 }
164 
165 static int
166 acpi_memory_check_device(struct acpi_memory_device *mem_device)
167 {
168 	unsigned long current_status;
169 
170 	ACPI_FUNCTION_TRACE("acpi_memory_check_device");
171 
172 	/* Get device present/absent information from the _STA */
173 	if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->handle, "_STA",
174 		NULL, &current_status)))
175 		return_VALUE(-ENODEV);
176 	/*
177 	 * Check for device status. Device should be
178 	 * present/enabled/functioning.
179 	 */
180 	if (!((current_status & ACPI_MEMORY_STA_PRESENT)
181 		&& (current_status & ACPI_MEMORY_STA_ENABLED)
182 		&& (current_status & ACPI_MEMORY_STA_FUNCTIONAL)))
183 		return_VALUE(-ENODEV);
184 
185 	return_VALUE(0);
186 }
187 
188 static int
189 acpi_memory_enable_device(struct acpi_memory_device *mem_device)
190 {
191 	int result;
192 
193 	ACPI_FUNCTION_TRACE("acpi_memory_enable_device");
194 
195 	/* Get the range from the _CRS */
196 	result = acpi_memory_get_device_resources(mem_device);
197 	if (result) {
198 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
199 			"\nget_device_resources failed\n"));
200 		mem_device->state = MEMORY_INVALID_STATE;
201 		return result;
202 	}
203 
204 	/*
205 	 * Tell the VM there is more memory here...
206 	 * Note: Assume that this function returns zero on success
207 	 */
208 	result = add_memory(mem_device->start_addr,
209 			(mem_device->end_addr - mem_device->start_addr) + 1,
210 			mem_device->read_write_attribute);
211 	if (result) {
212 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
213 			"\nadd_memory failed\n"));
214 		mem_device->state = MEMORY_INVALID_STATE;
215 		return result;
216 	}
217 
218 	return result;
219 }
220 
221 static int
222 acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
223 {
224 	acpi_status status;
225 	struct acpi_object_list	arg_list;
226 	union acpi_object arg;
227 	unsigned long current_status;
228 
229 	ACPI_FUNCTION_TRACE("acpi_memory_powerdown_device");
230 
231 	/* Issue the _EJ0 command */
232 	arg_list.count = 1;
233 	arg_list.pointer = &arg;
234 	arg.type = ACPI_TYPE_INTEGER;
235 	arg.integer.value = 1;
236 	status = acpi_evaluate_object(mem_device->handle,
237 			"_EJ0", &arg_list, NULL);
238 	/* Return on _EJ0 failure */
239 	if (ACPI_FAILURE(status)) {
240 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"_EJ0 failed.\n"));
241 		return_VALUE(-ENODEV);
242 	}
243 
244 	/* Evalute _STA to check if the device is disabled */
245 	status = acpi_evaluate_integer(mem_device->handle, "_STA",
246 		NULL, &current_status);
247 	if (ACPI_FAILURE(status))
248 		return_VALUE(-ENODEV);
249 
250 	/* Check for device status.  Device should be disabled */
251 	if (current_status & ACPI_MEMORY_STA_ENABLED)
252 		return_VALUE(-EINVAL);
253 
254 	return_VALUE(0);
255 }
256 
257 static int
258 acpi_memory_disable_device(struct acpi_memory_device *mem_device)
259 {
260 	int result;
261 	u64 start = mem_device->start_addr;
262 	u64 len = mem_device->end_addr - start + 1;
263 	unsigned long attr = mem_device->read_write_attribute;
264 
265 	ACPI_FUNCTION_TRACE("acpi_memory_disable_device");
266 
267 	/*
268 	 * Ask the VM to offline this memory range.
269 	 * Note: Assume that this function returns zero on success
270 	 */
271 	result = remove_memory(start, len, attr);
272 	if (result) {
273 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n"));
274 		return_VALUE(result);
275 	}
276 
277 	/* Power-off and eject the device */
278 	result = acpi_memory_powerdown_device(mem_device);
279 	if (result) {
280 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
281 					"Device Power Down failed.\n"));
282 		/* Set the status of the device to invalid */
283 		mem_device->state = MEMORY_INVALID_STATE;
284 		return result;
285 	}
286 
287 	mem_device->state = MEMORY_POWER_OFF_STATE;
288 	return result;
289 }
290 
291 static void
292 acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
293 {
294 	struct acpi_memory_device *mem_device;
295 	struct acpi_device *device;
296 
297 	ACPI_FUNCTION_TRACE("acpi_memory_device_notify");
298 
299 	switch (event) {
300 	case ACPI_NOTIFY_BUS_CHECK:
301 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
302 			"\nReceived BUS CHECK notification for device\n"));
303 		/* Fall Through */
304 	case ACPI_NOTIFY_DEVICE_CHECK:
305 		if (event == ACPI_NOTIFY_DEVICE_CHECK)
306 			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
307 			"\nReceived DEVICE CHECK notification for device\n"));
308 		if (acpi_memory_get_device(handle, &mem_device)) {
309 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
310 				"Error in finding driver data\n"));
311 			return_VOID;
312 		}
313 
314 		if (!acpi_memory_check_device(mem_device)) {
315 			if (acpi_memory_enable_device(mem_device))
316 				ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
317 				"Error in acpi_memory_enable_device\n"));
318 		}
319 		break;
320 	case ACPI_NOTIFY_EJECT_REQUEST:
321 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
322 			"\nReceived EJECT REQUEST notification for device\n"));
323 
324 		if (acpi_bus_get_device(handle, &device)) {
325 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
326 					"Device doesn't exist\n"));
327 			break;
328 		}
329 		mem_device = acpi_driver_data(device);
330 		if (!mem_device) {
331 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
332 					"Driver Data is NULL\n"));
333 			break;
334 		}
335 
336 		/*
337 		 * Currently disabling memory device from kernel mode
338 		 * TBD: Can also be disabled from user mode scripts
339 		 * TBD: Can also be disabled by Callback registration
340 		 * 	with generic sysfs driver
341 		 */
342 		if (acpi_memory_disable_device(mem_device))
343 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
344 				"Error in acpi_memory_disable_device\n"));
345 		/*
346 		 * TBD: Invoke acpi_bus_remove to cleanup data structures
347 		 */
348 		break;
349 	default:
350 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
351 			"Unsupported event [0x%x]\n", event));
352 		break;
353 	}
354 
355 	return_VOID;
356 }
357 
358 static int
359 acpi_memory_device_add(struct acpi_device *device)
360 {
361 	int result;
362 	struct acpi_memory_device *mem_device = NULL;
363 
364 	ACPI_FUNCTION_TRACE("acpi_memory_device_add");
365 
366 	if (!device)
367 		return_VALUE(-EINVAL);
368 
369 	mem_device = kmalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
370 	if (!mem_device)
371 		return_VALUE(-ENOMEM);
372 	memset(mem_device, 0, sizeof(struct acpi_memory_device));
373 
374 	mem_device->handle = device->handle;
375 	sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
376 	sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
377 	acpi_driver_data(device) = mem_device;
378 
379 	/* Get the range from the _CRS */
380 	result = acpi_memory_get_device_resources(mem_device);
381 	if (result) {
382 		kfree(mem_device);
383 		return_VALUE(result);
384 	}
385 
386 	/* Set the device state */
387 	mem_device->state = MEMORY_POWER_ON_STATE;
388 
389 	printk(KERN_INFO "%s \n", acpi_device_name(device));
390 
391 	return_VALUE(result);
392 }
393 
394 static int
395 acpi_memory_device_remove (struct acpi_device *device, int type)
396 {
397 	struct acpi_memory_device *mem_device = NULL;
398 
399 	ACPI_FUNCTION_TRACE("acpi_memory_device_remove");
400 
401 	if (!device || !acpi_driver_data(device))
402 		return_VALUE(-EINVAL);
403 
404 	mem_device = (struct acpi_memory_device *) acpi_driver_data(device);
405 	kfree(mem_device);
406 
407 	return_VALUE(0);
408 }
409 
410 /*
411  * Helper function to check for memory device
412  */
413 static acpi_status
414 is_memory_device(acpi_handle handle)
415 {
416 	char *hardware_id;
417 	acpi_status status;
418 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
419 	struct acpi_device_info *info;
420 
421 	ACPI_FUNCTION_TRACE("is_memory_device");
422 
423 	status = acpi_get_object_info(handle, &buffer);
424 	if (ACPI_FAILURE(status))
425 		return_ACPI_STATUS(AE_ERROR);
426 
427 	info = buffer.pointer;
428 	if (!(info->valid & ACPI_VALID_HID)) {
429 		acpi_os_free(buffer.pointer);
430 		return_ACPI_STATUS(AE_ERROR);
431 	}
432 
433 	hardware_id = info->hardware_id.value;
434 	if ((hardware_id == NULL) ||
435 		(strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
436 		status = AE_ERROR;
437 
438 	acpi_os_free(buffer.pointer);
439 	return_ACPI_STATUS(status);
440 }
441 
442 static acpi_status
443 acpi_memory_register_notify_handler (acpi_handle handle,
444 	u32 level, void *ctxt, void **retv)
445 {
446 	acpi_status status;
447 
448 	ACPI_FUNCTION_TRACE("acpi_memory_register_notify_handler");
449 
450 	status = is_memory_device(handle);
451 	if (ACPI_FAILURE(status))
452 		return_ACPI_STATUS(AE_OK);	/* continue */
453 
454 	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
455 			acpi_memory_device_notify, NULL);
456 	if (ACPI_FAILURE(status)) {
457 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
458 			"Error installing notify handler\n"));
459 		return_ACPI_STATUS(AE_OK);	/* continue */
460 	}
461 
462 	return_ACPI_STATUS(status);
463 }
464 
465 static acpi_status
466 acpi_memory_deregister_notify_handler (acpi_handle handle,
467 			       u32 level, void *ctxt, void **retv)
468 {
469 	acpi_status status;
470 
471 	ACPI_FUNCTION_TRACE("acpi_memory_deregister_notify_handler");
472 
473 	status = is_memory_device(handle);
474 	if (ACPI_FAILURE(status))
475 		return_ACPI_STATUS(AE_OK);	/* continue */
476 
477 	status = acpi_remove_notify_handler(handle,
478 			ACPI_SYSTEM_NOTIFY, acpi_memory_device_notify);
479 	if (ACPI_FAILURE(status)) {
480 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
481 				"Error removing notify handler\n"));
482 		return_ACPI_STATUS(AE_OK);	/* continue */
483 	}
484 
485 	return_ACPI_STATUS(status);
486 }
487 
488 static int __init
489 acpi_memory_device_init (void)
490 {
491 	int result;
492 	acpi_status status;
493 
494 	ACPI_FUNCTION_TRACE("acpi_memory_device_init");
495 
496 	result = acpi_bus_register_driver(&acpi_memory_device_driver);
497 
498 	if (result < 0)
499 		return_VALUE(-ENODEV);
500 
501 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
502 				ACPI_UINT32_MAX,
503 				acpi_memory_register_notify_handler,
504 				NULL, NULL);
505 
506 	if (ACPI_FAILURE (status)) {
507 		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n"));
508 		acpi_bus_unregister_driver(&acpi_memory_device_driver);
509 		return_VALUE(-ENODEV);
510         }
511 
512 	return_VALUE(0);
513 }
514 
515 static void __exit
516 acpi_memory_device_exit (void)
517 {
518 	acpi_status status;
519 
520 	ACPI_FUNCTION_TRACE("acpi_memory_device_exit");
521 
522 	/*
523 	 * Adding this to un-install notification handlers for all the device
524 	 * handles.
525 	 */
526 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
527 			ACPI_UINT32_MAX,
528 			acpi_memory_deregister_notify_handler,
529 			NULL, NULL);
530 
531 	if (ACPI_FAILURE (status))
532 		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n"));
533 
534 	acpi_bus_unregister_driver(&acpi_memory_device_driver);
535 
536 	return_VOID;
537 }
538 
539 module_init(acpi_memory_device_init);
540 module_exit(acpi_memory_device_exit);
541 
542 
543