OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [acpi/] [acpi_memhotplug.c] - Blame information for rev 78

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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
#define ACPI_MEMORY_DEVICE_COMPONENT            0x08000000UL
36
#define ACPI_MEMORY_DEVICE_CLASS                "memory"
37
#define ACPI_MEMORY_DEVICE_HID                  "PNP0C80"
38
#define ACPI_MEMORY_DEVICE_NAME                 "Hotplug Mem Device"
39
 
40
#define _COMPONENT              ACPI_MEMORY_DEVICE_COMPONENT
41
 
42
ACPI_MODULE_NAME("acpi_memhotplug");
43
MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
44
MODULE_DESCRIPTION("Hotplug Mem Driver");
45
MODULE_LICENSE("GPL");
46
 
47
/* Memory Device States */
48
#define MEMORY_INVALID_STATE    0
49
#define MEMORY_POWER_ON_STATE   1
50
#define MEMORY_POWER_OFF_STATE  2
51
 
52
static int acpi_memory_device_add(struct acpi_device *device);
53
static int acpi_memory_device_remove(struct acpi_device *device, int type);
54
static int acpi_memory_device_start(struct acpi_device *device);
55
 
56
static const struct acpi_device_id memory_device_ids[] = {
57
        {ACPI_MEMORY_DEVICE_HID, 0},
58
        {"", 0},
59
};
60
MODULE_DEVICE_TABLE(acpi, memory_device_ids);
61
 
62
static struct acpi_driver acpi_memory_device_driver = {
63
        .name = "acpi_memhotplug",
64
        .class = ACPI_MEMORY_DEVICE_CLASS,
65
        .ids = memory_device_ids,
66
        .ops = {
67
                .add = acpi_memory_device_add,
68
                .remove = acpi_memory_device_remove,
69
                .start = acpi_memory_device_start,
70
                },
71
};
72
 
73
struct acpi_memory_info {
74
        struct list_head list;
75
        u64 start_addr;         /* Memory Range start physical addr */
76
        u64 length;             /* Memory Range length */
77
        unsigned short caching; /* memory cache attribute */
78
        unsigned short write_protect;   /* memory read/write attribute */
79
        unsigned int enabled:1;
80
};
81
 
82
struct acpi_memory_device {
83
        struct acpi_device * device;
84
        unsigned int state;     /* State of the memory device */
85
        struct list_head res_list;
86
};
87
 
88
static int acpi_hotmem_initialized;
89
 
90
static acpi_status
91
acpi_memory_get_resource(struct acpi_resource *resource, void *context)
92
{
93
        struct acpi_memory_device *mem_device = context;
94
        struct acpi_resource_address64 address64;
95
        struct acpi_memory_info *info, *new;
96
        acpi_status status;
97
 
98
        status = acpi_resource_to_address64(resource, &address64);
99
        if (ACPI_FAILURE(status) ||
100
            (address64.resource_type != ACPI_MEMORY_RANGE))
101
                return AE_OK;
102
 
103
        list_for_each_entry(info, &mem_device->res_list, list) {
104
                /* Can we combine the resource range information? */
105
                if ((info->caching == address64.info.mem.caching) &&
106
                    (info->write_protect == address64.info.mem.write_protect) &&
107
                    (info->start_addr + info->length == address64.minimum)) {
108
                        info->length += address64.address_length;
109
                        return AE_OK;
110
                }
111
        }
112
 
113
        new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
114
        if (!new)
115
                return AE_ERROR;
116
 
117
        INIT_LIST_HEAD(&new->list);
118
        new->caching = address64.info.mem.caching;
119
        new->write_protect = address64.info.mem.write_protect;
120
        new->start_addr = address64.minimum;
121
        new->length = address64.address_length;
122
        list_add_tail(&new->list, &mem_device->res_list);
123
 
124
        return AE_OK;
125
}
126
 
127
static int
128
acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
129
{
130
        acpi_status status;
131
        struct acpi_memory_info *info, *n;
132
 
133
 
134
        if (!list_empty(&mem_device->res_list))
135
                return 0;
136
 
137
        status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
138
                                     acpi_memory_get_resource, mem_device);
139
        if (ACPI_FAILURE(status)) {
140
                list_for_each_entry_safe(info, n, &mem_device->res_list, list)
141
                        kfree(info);
142
                INIT_LIST_HEAD(&mem_device->res_list);
143
                return -EINVAL;
144
        }
145
 
146
        return 0;
147
}
148
 
149
static int
150
acpi_memory_get_device(acpi_handle handle,
151
                       struct acpi_memory_device **mem_device)
152
{
153
        acpi_status status;
154
        acpi_handle phandle;
155
        struct acpi_device *device = NULL;
156
        struct acpi_device *pdevice = NULL;
157
 
158
 
159
        if (!acpi_bus_get_device(handle, &device) && device)
160
                goto end;
161
 
162
        status = acpi_get_parent(handle, &phandle);
163
        if (ACPI_FAILURE(status)) {
164
                ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent"));
165
                return -EINVAL;
166
        }
167
 
168
        /* Get the parent device */
169
        status = acpi_bus_get_device(phandle, &pdevice);
170
        if (ACPI_FAILURE(status)) {
171
                ACPI_EXCEPTION((AE_INFO, status, "Cannot get acpi bus device"));
172
                return -EINVAL;
173
        }
174
 
175
        /*
176
         * Now add the notified device.  This creates the acpi_device
177
         * and invokes .add function
178
         */
179
        status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
180
        if (ACPI_FAILURE(status)) {
181
                ACPI_EXCEPTION((AE_INFO, status, "Cannot add acpi bus"));
182
                return -EINVAL;
183
        }
184
 
185
      end:
186
        *mem_device = acpi_driver_data(device);
187
        if (!(*mem_device)) {
188
                printk(KERN_ERR "\n driver data not found");
189
                return -ENODEV;
190
        }
191
 
192
        return 0;
193
}
194
 
195
static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
196
{
197
        unsigned long current_status;
198
 
199
 
200
        /* Get device present/absent information from the _STA */
201
        if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
202
                                               NULL, &current_status)))
203
                return -ENODEV;
204
        /*
205
         * Check for device status. Device should be
206
         * present/enabled/functioning.
207
         */
208
        if (!((current_status & ACPI_STA_DEVICE_PRESENT)
209
              && (current_status & ACPI_STA_DEVICE_ENABLED)
210
              && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
211
                return -ENODEV;
212
 
213
        return 0;
214
}
215
 
216
static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
217
{
218
        int result, num_enabled = 0;
219
        struct acpi_memory_info *info;
220
        int node;
221
 
222
 
223
        /* Get the range from the _CRS */
224
        result = acpi_memory_get_device_resources(mem_device);
225
        if (result) {
226
                printk(KERN_ERR PREFIX "get_device_resources failed\n");
227
                mem_device->state = MEMORY_INVALID_STATE;
228
                return result;
229
        }
230
 
231
        node = acpi_get_node(mem_device->device->handle);
232
        /*
233
         * Tell the VM there is more memory here...
234
         * Note: Assume that this function returns zero on success
235
         * We don't have memory-hot-add rollback function,now.
236
         * (i.e. memory-hot-remove function)
237
         */
238
        list_for_each_entry(info, &mem_device->res_list, list) {
239
                if (info->enabled) { /* just sanity check...*/
240
                        num_enabled++;
241
                        continue;
242
                }
243
 
244
                if (node < 0)
245
                        node = memory_add_physaddr_to_nid(info->start_addr);
246
 
247
                result = add_memory(node, info->start_addr, info->length);
248
                if (result)
249
                        continue;
250
                info->enabled = 1;
251
                num_enabled++;
252
        }
253
        if (!num_enabled) {
254
                printk(KERN_ERR PREFIX "add_memory failed\n");
255
                mem_device->state = MEMORY_INVALID_STATE;
256
                return -EINVAL;
257
        }
258
 
259
        return result;
260
}
261
 
262
static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
263
{
264
        acpi_status status;
265
        struct acpi_object_list arg_list;
266
        union acpi_object arg;
267
        unsigned long current_status;
268
 
269
 
270
        /* Issue the _EJ0 command */
271
        arg_list.count = 1;
272
        arg_list.pointer = &arg;
273
        arg.type = ACPI_TYPE_INTEGER;
274
        arg.integer.value = 1;
275
        status = acpi_evaluate_object(mem_device->device->handle,
276
                                      "_EJ0", &arg_list, NULL);
277
        /* Return on _EJ0 failure */
278
        if (ACPI_FAILURE(status)) {
279
                ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed"));
280
                return -ENODEV;
281
        }
282
 
283
        /* Evalute _STA to check if the device is disabled */
284
        status = acpi_evaluate_integer(mem_device->device->handle, "_STA",
285
                                       NULL, &current_status);
286
        if (ACPI_FAILURE(status))
287
                return -ENODEV;
288
 
289
        /* Check for device status.  Device should be disabled */
290
        if (current_status & ACPI_STA_DEVICE_ENABLED)
291
                return -EINVAL;
292
 
293
        return 0;
294
}
295
 
296
static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
297
{
298
        int result;
299
        struct acpi_memory_info *info, *n;
300
 
301
 
302
        /*
303
         * Ask the VM to offline this memory range.
304
         * Note: Assume that this function returns zero on success
305
         */
306
        list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
307
                if (info->enabled) {
308
                        result = remove_memory(info->start_addr, info->length);
309
                        if (result)
310
                                return result;
311
                }
312
                kfree(info);
313
        }
314
 
315
        /* Power-off and eject the device */
316
        result = acpi_memory_powerdown_device(mem_device);
317
        if (result) {
318
                /* Set the status of the device to invalid */
319
                mem_device->state = MEMORY_INVALID_STATE;
320
                return result;
321
        }
322
 
323
        mem_device->state = MEMORY_POWER_OFF_STATE;
324
        return result;
325
}
326
 
327
static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
328
{
329
        struct acpi_memory_device *mem_device;
330
        struct acpi_device *device;
331
 
332
 
333
        switch (event) {
334
        case ACPI_NOTIFY_BUS_CHECK:
335
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
336
                                  "\nReceived BUS CHECK notification for device\n"));
337
                /* Fall Through */
338
        case ACPI_NOTIFY_DEVICE_CHECK:
339
                if (event == ACPI_NOTIFY_DEVICE_CHECK)
340
                        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
341
                                          "\nReceived DEVICE CHECK notification for device\n"));
342
                if (acpi_memory_get_device(handle, &mem_device)) {
343
                        printk(KERN_ERR PREFIX "Cannot find driver data\n");
344
                        return;
345
                }
346
 
347
                if (!acpi_memory_check_device(mem_device)) {
348
                        if (acpi_memory_enable_device(mem_device))
349
                                printk(KERN_ERR PREFIX
350
                                            "Cannot enable memory device\n");
351
                }
352
                break;
353
        case ACPI_NOTIFY_EJECT_REQUEST:
354
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
355
                                  "\nReceived EJECT REQUEST notification for device\n"));
356
 
357
                if (acpi_bus_get_device(handle, &device)) {
358
                        printk(KERN_ERR PREFIX "Device doesn't exist\n");
359
                        break;
360
                }
361
                mem_device = acpi_driver_data(device);
362
                if (!mem_device) {
363
                        printk(KERN_ERR PREFIX "Driver Data is NULL\n");
364
                        break;
365
                }
366
 
367
                /*
368
                 * Currently disabling memory device from kernel mode
369
                 * TBD: Can also be disabled from user mode scripts
370
                 * TBD: Can also be disabled by Callback registration
371
                 *      with generic sysfs driver
372
                 */
373
                if (acpi_memory_disable_device(mem_device))
374
                        printk(KERN_ERR PREFIX
375
                                    "Disable memory device\n");
376
                /*
377
                 * TBD: Invoke acpi_bus_remove to cleanup data structures
378
                 */
379
                break;
380
        default:
381
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
382
                                  "Unsupported event [0x%x]\n", event));
383
                break;
384
        }
385
 
386
        return;
387
}
388
 
389
static int acpi_memory_device_add(struct acpi_device *device)
390
{
391
        int result;
392
        struct acpi_memory_device *mem_device = NULL;
393
 
394
 
395
        if (!device)
396
                return -EINVAL;
397
 
398
        mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
399
        if (!mem_device)
400
                return -ENOMEM;
401
 
402
        INIT_LIST_HEAD(&mem_device->res_list);
403
        mem_device->device = device;
404
        sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
405
        sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
406
        acpi_driver_data(device) = mem_device;
407
 
408
        /* Get the range from the _CRS */
409
        result = acpi_memory_get_device_resources(mem_device);
410
        if (result) {
411
                kfree(mem_device);
412
                return result;
413
        }
414
 
415
        /* Set the device state */
416
        mem_device->state = MEMORY_POWER_ON_STATE;
417
 
418
        printk(KERN_DEBUG "%s \n", acpi_device_name(device));
419
 
420
        return result;
421
}
422
 
423
static int acpi_memory_device_remove(struct acpi_device *device, int type)
424
{
425
        struct acpi_memory_device *mem_device = NULL;
426
 
427
 
428
        if (!device || !acpi_driver_data(device))
429
                return -EINVAL;
430
 
431
        mem_device = acpi_driver_data(device);
432
        kfree(mem_device);
433
 
434
        return 0;
435
}
436
 
437
static int acpi_memory_device_start (struct acpi_device *device)
438
{
439
        struct acpi_memory_device *mem_device;
440
        int result = 0;
441
 
442
        /*
443
         * Early boot code has recognized memory area by EFI/E820.
444
         * If DSDT shows these memory devices on boot, hotplug is not necessary
445
         * for them. So, it just returns until completion of this driver's
446
         * start up.
447
         */
448
        if (!acpi_hotmem_initialized)
449
                return 0;
450
 
451
        mem_device = acpi_driver_data(device);
452
 
453
        if (!acpi_memory_check_device(mem_device)) {
454
                /* call add_memory func */
455
                result = acpi_memory_enable_device(mem_device);
456
                if (result)
457
                        ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
458
                                "Error in acpi_memory_enable_device\n"));
459
        }
460
        return result;
461
}
462
 
463
/*
464
 * Helper function to check for memory device
465
 */
466
static acpi_status is_memory_device(acpi_handle handle)
467
{
468
        char *hardware_id;
469
        acpi_status status;
470
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
471
        struct acpi_device_info *info;
472
 
473
 
474
        status = acpi_get_object_info(handle, &buffer);
475
        if (ACPI_FAILURE(status))
476
                return status;
477
 
478
        info = buffer.pointer;
479
        if (!(info->valid & ACPI_VALID_HID)) {
480
                kfree(buffer.pointer);
481
                return AE_ERROR;
482
        }
483
 
484
        hardware_id = info->hardware_id.value;
485
        if ((hardware_id == NULL) ||
486
            (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
487
                status = AE_ERROR;
488
 
489
        kfree(buffer.pointer);
490
        return status;
491
}
492
 
493
static acpi_status
494
acpi_memory_register_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;   /* continue */
503
 
504
        status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
505
                                             acpi_memory_device_notify, NULL);
506
        /* continue */
507
        return AE_OK;
508
}
509
 
510
static acpi_status
511
acpi_memory_deregister_notify_handler(acpi_handle handle,
512
                                      u32 level, void *ctxt, void **retv)
513
{
514
        acpi_status status;
515
 
516
 
517
        status = is_memory_device(handle);
518
        if (ACPI_FAILURE(status))
519
                return AE_OK;   /* continue */
520
 
521
        status = acpi_remove_notify_handler(handle,
522
                                            ACPI_SYSTEM_NOTIFY,
523
                                            acpi_memory_device_notify);
524
 
525
        return AE_OK;   /* continue */
526
}
527
 
528
static int __init acpi_memory_device_init(void)
529
{
530
        int result;
531
        acpi_status status;
532
 
533
 
534
        result = acpi_bus_register_driver(&acpi_memory_device_driver);
535
 
536
        if (result < 0)
537
                return -ENODEV;
538
 
539
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
540
                                     ACPI_UINT32_MAX,
541
                                     acpi_memory_register_notify_handler,
542
                                     NULL, NULL);
543
 
544
        if (ACPI_FAILURE(status)) {
545
                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
546
                acpi_bus_unregister_driver(&acpi_memory_device_driver);
547
                return -ENODEV;
548
        }
549
 
550
        acpi_hotmem_initialized = 1;
551
        return 0;
552
}
553
 
554
static void __exit acpi_memory_device_exit(void)
555
{
556
        acpi_status status;
557
 
558
 
559
        /*
560
         * Adding this to un-install notification handlers for all the device
561
         * handles.
562
         */
563
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
564
                                     ACPI_UINT32_MAX,
565
                                     acpi_memory_deregister_notify_handler,
566
                                     NULL, NULL);
567
 
568
        if (ACPI_FAILURE(status))
569
                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
570
 
571
        acpi_bus_unregister_driver(&acpi_memory_device_driver);
572
 
573
        return;
574
}
575
 
576
module_init(acpi_memory_device_init);
577
module_exit(acpi_memory_device_exit);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.