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, ¤t_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, ¤t_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