diff options
Diffstat (limited to 'drivers/acpi/acpi_memhotplug.c')
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 328 |
1 files changed, 42 insertions, 286 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index da1f82b445e0..5e6301e94920 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c | |||
@@ -1,5 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com> | 2 | * Copyright (C) 2004, 2013 Intel Corporation |
3 | * Author: Naveen B S <naveen.b.s@intel.com> | ||
4 | * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | ||
3 | * | 5 | * |
4 | * All rights reserved. | 6 | * All rights reserved. |
5 | * | 7 | * |
@@ -25,14 +27,10 @@ | |||
25 | * ranges. | 27 | * ranges. |
26 | */ | 28 | */ |
27 | 29 | ||
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 <linux/slab.h> | ||
34 | #include <linux/acpi.h> | 30 | #include <linux/acpi.h> |
35 | #include <acpi/acpi_drivers.h> | 31 | #include <linux/memory_hotplug.h> |
32 | |||
33 | #include "internal.h" | ||
36 | 34 | ||
37 | #define ACPI_MEMORY_DEVICE_CLASS "memory" | 35 | #define ACPI_MEMORY_DEVICE_CLASS "memory" |
38 | #define ACPI_MEMORY_DEVICE_HID "PNP0C80" | 36 | #define ACPI_MEMORY_DEVICE_HID "PNP0C80" |
@@ -44,32 +42,28 @@ | |||
44 | #define PREFIX "ACPI:memory_hp:" | 42 | #define PREFIX "ACPI:memory_hp:" |
45 | 43 | ||
46 | ACPI_MODULE_NAME("acpi_memhotplug"); | 44 | ACPI_MODULE_NAME("acpi_memhotplug"); |
47 | MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); | ||
48 | MODULE_DESCRIPTION("Hotplug Mem Driver"); | ||
49 | MODULE_LICENSE("GPL"); | ||
50 | 45 | ||
51 | /* Memory Device States */ | 46 | /* Memory Device States */ |
52 | #define MEMORY_INVALID_STATE 0 | 47 | #define MEMORY_INVALID_STATE 0 |
53 | #define MEMORY_POWER_ON_STATE 1 | 48 | #define MEMORY_POWER_ON_STATE 1 |
54 | #define MEMORY_POWER_OFF_STATE 2 | 49 | #define MEMORY_POWER_OFF_STATE 2 |
55 | 50 | ||
56 | static int acpi_memory_device_add(struct acpi_device *device); | 51 | static int acpi_memory_device_add(struct acpi_device *device, |
57 | static int acpi_memory_device_remove(struct acpi_device *device); | 52 | const struct acpi_device_id *not_used); |
53 | static void acpi_memory_device_remove(struct acpi_device *device); | ||
58 | 54 | ||
59 | static const struct acpi_device_id memory_device_ids[] = { | 55 | static const struct acpi_device_id memory_device_ids[] = { |
60 | {ACPI_MEMORY_DEVICE_HID, 0}, | 56 | {ACPI_MEMORY_DEVICE_HID, 0}, |
61 | {"", 0}, | 57 | {"", 0}, |
62 | }; | 58 | }; |
63 | MODULE_DEVICE_TABLE(acpi, memory_device_ids); | ||
64 | 59 | ||
65 | static struct acpi_driver acpi_memory_device_driver = { | 60 | static struct acpi_scan_handler memory_device_handler = { |
66 | .name = "acpi_memhotplug", | ||
67 | .class = ACPI_MEMORY_DEVICE_CLASS, | ||
68 | .ids = memory_device_ids, | 61 | .ids = memory_device_ids, |
69 | .ops = { | 62 | .attach = acpi_memory_device_add, |
70 | .add = acpi_memory_device_add, | 63 | .detach = acpi_memory_device_remove, |
71 | .remove = acpi_memory_device_remove, | 64 | .hotplug = { |
72 | }, | 65 | .enabled = true, |
66 | }, | ||
73 | }; | 67 | }; |
74 | 68 | ||
75 | struct acpi_memory_info { | 69 | struct acpi_memory_info { |
@@ -79,7 +73,6 @@ struct acpi_memory_info { | |||
79 | unsigned short caching; /* memory cache attribute */ | 73 | unsigned short caching; /* memory cache attribute */ |
80 | unsigned short write_protect; /* memory read/write attribute */ | 74 | unsigned short write_protect; /* memory read/write attribute */ |
81 | unsigned int enabled:1; | 75 | unsigned int enabled:1; |
82 | unsigned int failed:1; | ||
83 | }; | 76 | }; |
84 | 77 | ||
85 | struct acpi_memory_device { | 78 | struct acpi_memory_device { |
@@ -153,48 +146,6 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) | |||
153 | return 0; | 146 | return 0; |
154 | } | 147 | } |
155 | 148 | ||
156 | static int acpi_memory_get_device(acpi_handle handle, | ||
157 | struct acpi_memory_device **mem_device) | ||
158 | { | ||
159 | struct acpi_device *device = NULL; | ||
160 | int result = 0; | ||
161 | |||
162 | acpi_scan_lock_acquire(); | ||
163 | |||
164 | acpi_bus_get_device(handle, &device); | ||
165 | if (device) | ||
166 | goto end; | ||
167 | |||
168 | /* | ||
169 | * Now add the notified device. This creates the acpi_device | ||
170 | * and invokes .add function | ||
171 | */ | ||
172 | result = acpi_bus_scan(handle); | ||
173 | if (result) { | ||
174 | acpi_handle_warn(handle, "ACPI namespace scan failed\n"); | ||
175 | result = -EINVAL; | ||
176 | goto out; | ||
177 | } | ||
178 | result = acpi_bus_get_device(handle, &device); | ||
179 | if (result) { | ||
180 | acpi_handle_warn(handle, "Missing device object\n"); | ||
181 | result = -EINVAL; | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | end: | ||
186 | *mem_device = acpi_driver_data(device); | ||
187 | if (!(*mem_device)) { | ||
188 | dev_err(&device->dev, "driver data not found\n"); | ||
189 | result = -ENODEV; | ||
190 | goto out; | ||
191 | } | ||
192 | |||
193 | out: | ||
194 | acpi_scan_lock_release(); | ||
195 | return result; | ||
196 | } | ||
197 | |||
198 | static int acpi_memory_check_device(struct acpi_memory_device *mem_device) | 149 | static int acpi_memory_check_device(struct acpi_memory_device *mem_device) |
199 | { | 150 | { |
200 | unsigned long long current_status; | 151 | unsigned long long current_status; |
@@ -249,13 +200,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
249 | * returns -EEXIST. If add_memory() returns the other error, it | 200 | * returns -EEXIST. If add_memory() returns the other error, it |
250 | * means that this memory block is not used by the kernel. | 201 | * means that this memory block is not used by the kernel. |
251 | */ | 202 | */ |
252 | if (result && result != -EEXIST) { | 203 | if (result && result != -EEXIST) |
253 | info->failed = 1; | ||
254 | continue; | 204 | continue; |
255 | } | ||
256 | 205 | ||
257 | if (!result) | 206 | info->enabled = 1; |
258 | info->enabled = 1; | 207 | |
259 | /* | 208 | /* |
260 | * Add num_enable even if add_memory() returns -EEXIST, so the | 209 | * Add num_enable even if add_memory() returns -EEXIST, so the |
261 | * device is bound to this driver. | 210 | * device is bound to this driver. |
@@ -286,16 +235,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) | |||
286 | nid = acpi_get_node(mem_device->device->handle); | 235 | nid = acpi_get_node(mem_device->device->handle); |
287 | 236 | ||
288 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { | 237 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { |
289 | if (info->failed) | ||
290 | /* The kernel does not use this memory block */ | ||
291 | continue; | ||
292 | |||
293 | if (!info->enabled) | 238 | if (!info->enabled) |
294 | /* | 239 | continue; |
295 | * The kernel uses this memory block, but it may be not | ||
296 | * managed by us. | ||
297 | */ | ||
298 | return -EBUSY; | ||
299 | 240 | ||
300 | if (nid < 0) | 241 | if (nid < 0) |
301 | nid = memory_add_physaddr_to_nid(info->start_addr); | 242 | nid = memory_add_physaddr_to_nid(info->start_addr); |
@@ -310,95 +251,21 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) | |||
310 | return result; | 251 | return result; |
311 | } | 252 | } |
312 | 253 | ||
313 | static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | ||
314 | { | ||
315 | struct acpi_memory_device *mem_device; | ||
316 | struct acpi_device *device; | ||
317 | struct acpi_eject_event *ej_event = NULL; | ||
318 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ | ||
319 | acpi_status status; | ||
320 | |||
321 | switch (event) { | ||
322 | case ACPI_NOTIFY_BUS_CHECK: | ||
323 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
324 | "\nReceived BUS CHECK notification for device\n")); | ||
325 | /* Fall Through */ | ||
326 | case ACPI_NOTIFY_DEVICE_CHECK: | ||
327 | if (event == ACPI_NOTIFY_DEVICE_CHECK) | ||
328 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
329 | "\nReceived DEVICE CHECK notification for device\n")); | ||
330 | if (acpi_memory_get_device(handle, &mem_device)) { | ||
331 | acpi_handle_err(handle, "Cannot find driver data\n"); | ||
332 | break; | ||
333 | } | ||
334 | |||
335 | ost_code = ACPI_OST_SC_SUCCESS; | ||
336 | break; | ||
337 | |||
338 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
339 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
340 | "\nReceived EJECT REQUEST notification for device\n")); | ||
341 | |||
342 | status = AE_ERROR; | ||
343 | acpi_scan_lock_acquire(); | ||
344 | |||
345 | if (acpi_bus_get_device(handle, &device)) { | ||
346 | acpi_handle_err(handle, "Device doesn't exist\n"); | ||
347 | goto unlock; | ||
348 | } | ||
349 | mem_device = acpi_driver_data(device); | ||
350 | if (!mem_device) { | ||
351 | acpi_handle_err(handle, "Driver Data is NULL\n"); | ||
352 | goto unlock; | ||
353 | } | ||
354 | |||
355 | ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); | ||
356 | if (!ej_event) { | ||
357 | pr_err(PREFIX "No memory, dropping EJECT\n"); | ||
358 | goto unlock; | ||
359 | } | ||
360 | |||
361 | get_device(&device->dev); | ||
362 | ej_event->device = device; | ||
363 | ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; | ||
364 | /* The eject is carried out asynchronously. */ | ||
365 | status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, | ||
366 | ej_event); | ||
367 | if (ACPI_FAILURE(status)) { | ||
368 | put_device(&device->dev); | ||
369 | kfree(ej_event); | ||
370 | } | ||
371 | |||
372 | unlock: | ||
373 | acpi_scan_lock_release(); | ||
374 | if (ACPI_SUCCESS(status)) | ||
375 | return; | ||
376 | default: | ||
377 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
378 | "Unsupported event [0x%x]\n", event)); | ||
379 | |||
380 | /* non-hotplug event; possibly handled by other handler */ | ||
381 | return; | ||
382 | } | ||
383 | |||
384 | /* Inform firmware that the hotplug operation has completed */ | ||
385 | (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); | ||
386 | } | ||
387 | |||
388 | static void acpi_memory_device_free(struct acpi_memory_device *mem_device) | 254 | static void acpi_memory_device_free(struct acpi_memory_device *mem_device) |
389 | { | 255 | { |
390 | if (!mem_device) | 256 | if (!mem_device) |
391 | return; | 257 | return; |
392 | 258 | ||
393 | acpi_memory_free_device_resources(mem_device); | 259 | acpi_memory_free_device_resources(mem_device); |
260 | mem_device->device->driver_data = NULL; | ||
394 | kfree(mem_device); | 261 | kfree(mem_device); |
395 | } | 262 | } |
396 | 263 | ||
397 | static int acpi_memory_device_add(struct acpi_device *device) | 264 | static int acpi_memory_device_add(struct acpi_device *device, |
265 | const struct acpi_device_id *not_used) | ||
398 | { | 266 | { |
267 | struct acpi_memory_device *mem_device; | ||
399 | int result; | 268 | int result; |
400 | struct acpi_memory_device *mem_device = NULL; | ||
401 | |||
402 | 269 | ||
403 | if (!device) | 270 | if (!device) |
404 | return -EINVAL; | 271 | return -EINVAL; |
@@ -423,147 +290,36 @@ static int acpi_memory_device_add(struct acpi_device *device) | |||
423 | /* Set the device state */ | 290 | /* Set the device state */ |
424 | mem_device->state = MEMORY_POWER_ON_STATE; | 291 | mem_device->state = MEMORY_POWER_ON_STATE; |
425 | 292 | ||
426 | pr_debug("%s\n", acpi_device_name(device)); | 293 | result = acpi_memory_check_device(mem_device); |
294 | if (result) { | ||
295 | acpi_memory_device_free(mem_device); | ||
296 | return 0; | ||
297 | } | ||
427 | 298 | ||
428 | if (!acpi_memory_check_device(mem_device)) { | 299 | result = acpi_memory_enable_device(mem_device); |
429 | /* call add_memory func */ | 300 | if (result) { |
430 | result = acpi_memory_enable_device(mem_device); | 301 | dev_err(&device->dev, "acpi_memory_enable_device() error\n"); |
431 | if (result) { | 302 | acpi_memory_device_free(mem_device); |
432 | dev_err(&device->dev, | 303 | return -ENODEV; |
433 | "Error in acpi_memory_enable_device\n"); | ||
434 | acpi_memory_device_free(mem_device); | ||
435 | } | ||
436 | } | 304 | } |
437 | return result; | 305 | |
306 | dev_dbg(&device->dev, "Memory device configured by ACPI\n"); | ||
307 | return 1; | ||
438 | } | 308 | } |
439 | 309 | ||
440 | static int acpi_memory_device_remove(struct acpi_device *device) | 310 | static void acpi_memory_device_remove(struct acpi_device *device) |
441 | { | 311 | { |
442 | struct acpi_memory_device *mem_device = NULL; | 312 | struct acpi_memory_device *mem_device; |
443 | int result; | ||
444 | 313 | ||
445 | if (!device || !acpi_driver_data(device)) | 314 | if (!device || !acpi_driver_data(device)) |
446 | return -EINVAL; | 315 | return; |
447 | 316 | ||
448 | mem_device = acpi_driver_data(device); | 317 | mem_device = acpi_driver_data(device); |
449 | 318 | acpi_memory_remove_memory(mem_device); | |
450 | result = acpi_memory_remove_memory(mem_device); | ||
451 | if (result) | ||
452 | return result; | ||
453 | |||
454 | acpi_memory_device_free(mem_device); | 319 | acpi_memory_device_free(mem_device); |
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | /* | ||
460 | * Helper function to check for memory device | ||
461 | */ | ||
462 | static acpi_status is_memory_device(acpi_handle handle) | ||
463 | { | ||
464 | char *hardware_id; | ||
465 | acpi_status status; | ||
466 | struct acpi_device_info *info; | ||
467 | |||
468 | status = acpi_get_object_info(handle, &info); | ||
469 | if (ACPI_FAILURE(status)) | ||
470 | return status; | ||
471 | |||
472 | if (!(info->valid & ACPI_VALID_HID)) { | ||
473 | kfree(info); | ||
474 | return AE_ERROR; | ||
475 | } | ||
476 | |||
477 | hardware_id = info->hardware_id.string; | ||
478 | if ((hardware_id == NULL) || | ||
479 | (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) | ||
480 | status = AE_ERROR; | ||
481 | |||
482 | kfree(info); | ||
483 | return status; | ||
484 | } | ||
485 | |||
486 | static acpi_status | ||
487 | acpi_memory_register_notify_handler(acpi_handle handle, | ||
488 | u32 level, void *ctxt, void **retv) | ||
489 | { | ||
490 | acpi_status status; | ||
491 | |||
492 | |||
493 | status = is_memory_device(handle); | ||
494 | if (ACPI_FAILURE(status)) | ||
495 | return AE_OK; /* continue */ | ||
496 | |||
497 | status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | ||
498 | acpi_memory_device_notify, NULL); | ||
499 | /* continue */ | ||
500 | return AE_OK; | ||
501 | } | ||
502 | |||
503 | static acpi_status | ||
504 | acpi_memory_deregister_notify_handler(acpi_handle handle, | ||
505 | u32 level, void *ctxt, void **retv) | ||
506 | { | ||
507 | acpi_status status; | ||
508 | |||
509 | |||
510 | status = is_memory_device(handle); | ||
511 | if (ACPI_FAILURE(status)) | ||
512 | return AE_OK; /* continue */ | ||
513 | |||
514 | status = acpi_remove_notify_handler(handle, | ||
515 | ACPI_SYSTEM_NOTIFY, | ||
516 | acpi_memory_device_notify); | ||
517 | |||
518 | return AE_OK; /* continue */ | ||
519 | } | ||
520 | |||
521 | static int __init acpi_memory_device_init(void) | ||
522 | { | ||
523 | int result; | ||
524 | acpi_status status; | ||
525 | |||
526 | |||
527 | result = acpi_bus_register_driver(&acpi_memory_device_driver); | ||
528 | |||
529 | if (result < 0) | ||
530 | return -ENODEV; | ||
531 | |||
532 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
533 | ACPI_UINT32_MAX, | ||
534 | acpi_memory_register_notify_handler, NULL, | ||
535 | NULL, NULL); | ||
536 | |||
537 | if (ACPI_FAILURE(status)) { | ||
538 | ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); | ||
539 | acpi_bus_unregister_driver(&acpi_memory_device_driver); | ||
540 | return -ENODEV; | ||
541 | } | ||
542 | |||
543 | return 0; | ||
544 | } | 320 | } |
545 | 321 | ||
546 | static void __exit acpi_memory_device_exit(void) | 322 | void __init acpi_memory_hotplug_init(void) |
547 | { | 323 | { |
548 | acpi_status status; | 324 | acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory"); |
549 | |||
550 | |||
551 | /* | ||
552 | * Adding this to un-install notification handlers for all the device | ||
553 | * handles. | ||
554 | */ | ||
555 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
556 | ACPI_UINT32_MAX, | ||
557 | acpi_memory_deregister_notify_handler, NULL, | ||
558 | NULL, NULL); | ||
559 | |||
560 | if (ACPI_FAILURE(status)) | ||
561 | ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed")); | ||
562 | |||
563 | acpi_bus_unregister_driver(&acpi_memory_device_driver); | ||
564 | |||
565 | return; | ||
566 | } | 325 | } |
567 | |||
568 | module_init(acpi_memory_device_init); | ||
569 | module_exit(acpi_memory_device_exit); | ||