1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 <linux/slab.h>
34#include <linux/acpi.h>
35#include <acpi/acpi_drivers.h>
36
37#define ACPI_MEMORY_DEVICE_CLASS "memory"
38#define ACPI_MEMORY_DEVICE_HID "PNP0C80"
39#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device"
40
41#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT
42
43#undef PREFIX
44#define PREFIX "ACPI:memory_hp:"
45
46ACPI_MODULE_NAME("acpi_memhotplug");
47MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
48MODULE_DESCRIPTION("Hotplug Mem Driver");
49MODULE_LICENSE("GPL");
50
51
52#define MEMORY_INVALID_STATE 0
53#define MEMORY_POWER_ON_STATE 1
54#define MEMORY_POWER_OFF_STATE 2
55
56static int acpi_memory_device_add(struct acpi_device *device);
57static int acpi_memory_device_remove(struct acpi_device *device, int type);
58
59static const struct acpi_device_id memory_device_ids[] = {
60 {ACPI_MEMORY_DEVICE_HID, 0},
61 {"", 0},
62};
63MODULE_DEVICE_TABLE(acpi, memory_device_ids);
64
65static struct acpi_driver acpi_memory_device_driver = {
66 .name = "acpi_memhotplug",
67 .class = ACPI_MEMORY_DEVICE_CLASS,
68 .ids = memory_device_ids,
69 .ops = {
70 .add = acpi_memory_device_add,
71 .remove = acpi_memory_device_remove,
72 },
73};
74
75struct acpi_memory_info {
76 struct list_head list;
77 u64 start_addr;
78 u64 length;
79 unsigned short caching;
80 unsigned short write_protect;
81 unsigned int enabled:1;
82 unsigned int failed:1;
83};
84
85struct acpi_memory_device {
86 struct acpi_device * device;
87 unsigned int state;
88 struct list_head res_list;
89};
90
91static acpi_status
92acpi_memory_get_resource(struct acpi_resource *resource, void *context)
93{
94 struct acpi_memory_device *mem_device = context;
95 struct acpi_resource_address64 address64;
96 struct acpi_memory_info *info, *new;
97 acpi_status status;
98
99 status = acpi_resource_to_address64(resource, &address64);
100 if (ACPI_FAILURE(status) ||
101 (address64.resource_type != ACPI_MEMORY_RANGE))
102 return AE_OK;
103
104 list_for_each_entry(info, &mem_device->res_list, list) {
105
106 if ((info->caching == address64.info.mem.caching) &&
107 (info->write_protect == address64.info.mem.write_protect) &&
108 (info->start_addr + info->length == address64.minimum)) {
109 info->length += address64.address_length;
110 return AE_OK;
111 }
112 }
113
114 new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
115 if (!new)
116 return AE_ERROR;
117
118 INIT_LIST_HEAD(&new->list);
119 new->caching = address64.info.mem.caching;
120 new->write_protect = address64.info.mem.write_protect;
121 new->start_addr = address64.minimum;
122 new->length = address64.address_length;
123 list_add_tail(&new->list, &mem_device->res_list);
124
125 return AE_OK;
126}
127
128static void
129acpi_memory_free_device_resources(struct acpi_memory_device *mem_device)
130{
131 struct acpi_memory_info *info, *n;
132
133 list_for_each_entry_safe(info, n, &mem_device->res_list, list)
134 kfree(info);
135 INIT_LIST_HEAD(&mem_device->res_list);
136}
137
138static int
139acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
140{
141 acpi_status status;
142
143 if (!list_empty(&mem_device->res_list))
144 return 0;
145
146 status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
147 acpi_memory_get_resource, mem_device);
148 if (ACPI_FAILURE(status)) {
149 acpi_memory_free_device_resources(mem_device);
150 return -EINVAL;
151 }
152
153 return 0;
154}
155
156static int
157acpi_memory_get_device(acpi_handle handle,
158 struct acpi_memory_device **mem_device)
159{
160 acpi_status status;
161 acpi_handle phandle;
162 struct acpi_device *device = NULL;
163 struct acpi_device *pdevice = NULL;
164 int result;
165
166
167 if (!acpi_bus_get_device(handle, &device) && device)
168 goto end;
169
170 status = acpi_get_parent(handle, &phandle);
171 if (ACPI_FAILURE(status)) {
172 ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent"));
173 return -EINVAL;
174 }
175
176
177 result = acpi_bus_get_device(phandle, &pdevice);
178 if (result) {
179 acpi_handle_warn(phandle, "Cannot get acpi bus device\n");
180 return -EINVAL;
181 }
182
183
184
185
186
187 result = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
188 if (result) {
189 acpi_handle_warn(handle, "Cannot add acpi bus\n");
190 return -EINVAL;
191 }
192
193 end:
194 *mem_device = acpi_driver_data(device);
195 if (!(*mem_device)) {
196 dev_err(&device->dev, "driver data not found\n");
197 return -ENODEV;
198 }
199
200 return 0;
201}
202
203static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
204{
205 unsigned long long current_status;
206
207
208 if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
209 NULL, ¤t_status)))
210 return -ENODEV;
211
212
213
214
215 if (!((current_status & ACPI_STA_DEVICE_PRESENT)
216 && (current_status & ACPI_STA_DEVICE_ENABLED)
217 && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
218 return -ENODEV;
219
220 return 0;
221}
222
223static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
224{
225 int result, num_enabled = 0;
226 struct acpi_memory_info *info;
227 int node;
228
229 node = acpi_get_node(mem_device->device->handle);
230
231
232
233
234
235
236 list_for_each_entry(info, &mem_device->res_list, list) {
237 if (info->enabled) {
238 num_enabled++;
239 continue;
240 }
241
242
243
244
245 if (!info->length)
246 continue;
247 if (node < 0)
248 node = memory_add_physaddr_to_nid(info->start_addr);
249
250 result = add_memory(node, info->start_addr, info->length);
251
252
253
254
255
256
257 if (result && result != -EEXIST) {
258 info->failed = 1;
259 continue;
260 }
261
262 if (!result)
263 info->enabled = 1;
264
265
266
267
268 num_enabled++;
269 }
270 if (!num_enabled) {
271 dev_err(&mem_device->device->dev, "add_memory failed\n");
272 mem_device->state = MEMORY_INVALID_STATE;
273 return -EINVAL;
274 }
275
276
277
278
279
280
281
282
283 return 0;
284}
285
286static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
287{
288 int result = 0;
289 struct acpi_memory_info *info, *n;
290
291 list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
292 if (info->failed)
293
294 continue;
295
296 if (!info->enabled)
297
298
299
300
301 return -EBUSY;
302
303 result = remove_memory(info->start_addr, info->length);
304 if (result)
305 return result;
306
307 list_del(&info->list);
308 kfree(info);
309 }
310
311 return result;
312}
313
314static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
315{
316 struct acpi_memory_device *mem_device;
317 struct acpi_device *device;
318 struct acpi_eject_event *ej_event = NULL;
319 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
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
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 if (acpi_bus_get_device(handle, &device)) {
343 acpi_handle_err(handle, "Device doesn't exist\n");
344 break;
345 }
346 mem_device = acpi_driver_data(device);
347 if (!mem_device) {
348 acpi_handle_err(handle, "Driver Data is NULL\n");
349 break;
350 }
351
352 ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
353 if (!ej_event) {
354 pr_err(PREFIX "No memory, dropping EJECT\n");
355 break;
356 }
357
358 ej_event->handle = handle;
359 ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
360 acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
361 (void *)ej_event);
362
363
364 return;
365 default:
366 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
367 "Unsupported event [0x%x]\n", event));
368
369
370 return;
371 }
372
373
374 (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
375 return;
376}
377
378static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
379{
380 if (!mem_device)
381 return;
382
383 acpi_memory_free_device_resources(mem_device);
384 kfree(mem_device);
385}
386
387static int acpi_memory_device_add(struct acpi_device *device)
388{
389 int result;
390 struct acpi_memory_device *mem_device = NULL;
391
392
393 if (!device)
394 return -EINVAL;
395
396 mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
397 if (!mem_device)
398 return -ENOMEM;
399
400 INIT_LIST_HEAD(&mem_device->res_list);
401 mem_device->device = device;
402 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
403 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
404 device->driver_data = mem_device;
405
406
407 result = acpi_memory_get_device_resources(mem_device);
408 if (result) {
409 kfree(mem_device);
410 return result;
411 }
412
413
414 mem_device->state = MEMORY_POWER_ON_STATE;
415
416 pr_debug("%s\n", acpi_device_name(device));
417
418 if (!acpi_memory_check_device(mem_device)) {
419
420 result = acpi_memory_enable_device(mem_device);
421 if (result) {
422 dev_err(&device->dev,
423 "Error in acpi_memory_enable_device\n");
424 acpi_memory_device_free(mem_device);
425 }
426 }
427 return result;
428}
429
430static int acpi_memory_device_remove(struct acpi_device *device, int type)
431{
432 struct acpi_memory_device *mem_device = NULL;
433 int result;
434
435 if (!device || !acpi_driver_data(device))
436 return -EINVAL;
437
438 mem_device = acpi_driver_data(device);
439
440 result = acpi_memory_remove_memory(mem_device);
441 if (result)
442 return result;
443
444 acpi_memory_device_free(mem_device);
445
446 return 0;
447}
448
449
450
451
452static acpi_status is_memory_device(acpi_handle handle)
453{
454 char *hardware_id;
455 acpi_status status;
456 struct acpi_device_info *info;
457
458 status = acpi_get_object_info(handle, &info);
459 if (ACPI_FAILURE(status))
460 return status;
461
462 if (!(info->valid & ACPI_VALID_HID)) {
463 kfree(info);
464 return AE_ERROR;
465 }
466
467 hardware_id = info->hardware_id.string;
468 if ((hardware_id == NULL) ||
469 (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
470 status = AE_ERROR;
471
472 kfree(info);
473 return status;
474}
475
476static acpi_status
477acpi_memory_register_notify_handler(acpi_handle handle,
478 u32 level, void *ctxt, void **retv)
479{
480 acpi_status status;
481
482
483 status = is_memory_device(handle);
484 if (ACPI_FAILURE(status))
485 return AE_OK;
486
487 status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
488 acpi_memory_device_notify, NULL);
489
490 return AE_OK;
491}
492
493static acpi_status
494acpi_memory_deregister_notify_handler(acpi_handle handle,
495 u32 level, void *ctxt, void **retv)
496{
497 acpi_status status;
498
499
500 status = is_memory_device(handle);
501 if (ACPI_FAILURE(status))
502 return AE_OK;
503
504 status = acpi_remove_notify_handler(handle,
505 ACPI_SYSTEM_NOTIFY,
506 acpi_memory_device_notify);
507
508 return AE_OK;
509}
510
511static int __init acpi_memory_device_init(void)
512{
513 int result;
514 acpi_status status;
515
516
517 result = acpi_bus_register_driver(&acpi_memory_device_driver);
518
519 if (result < 0)
520 return -ENODEV;
521
522 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
523 ACPI_UINT32_MAX,
524 acpi_memory_register_notify_handler, NULL,
525 NULL, NULL);
526
527 if (ACPI_FAILURE(status)) {
528 ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
529 acpi_bus_unregister_driver(&acpi_memory_device_driver);
530 return -ENODEV;
531 }
532
533 return 0;
534}
535
536static void __exit acpi_memory_device_exit(void)
537{
538 acpi_status status;
539
540
541
542
543
544
545 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
546 ACPI_UINT32_MAX,
547 acpi_memory_deregister_notify_handler, NULL,
548 NULL, NULL);
549
550 if (ACPI_FAILURE(status))
551 ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
552
553 acpi_bus_unregister_driver(&acpi_memory_device_driver);
554
555 return;
556}
557
558module_init(acpi_memory_device_init);
559module_exit(acpi_memory_device_exit);
560