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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [acpi/] [power.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *  acpi_power.c - ACPI Bus Power Management ($Revision: 1.1.1.1 $)
3
 *
4
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6
 *
7
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or (at
12
 *  your option) any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful, but
15
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 *  General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License along
20
 *  with this program; if not, write to the Free Software Foundation, Inc.,
21
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22
 *
23
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24
 */
25
 
26
#include <linux/kernel.h>
27
#include <linux/module.h>
28
#include <linux/init.h>
29
#include <linux/types.h>
30
#include <linux/compatmac.h>
31
#include <linux/proc_fs.h>
32
#include <acpi/acpi_bus.h>
33
#include <acpi/acpi_drivers.h>
34
 
35
 
36
#define _COMPONENT              ACPI_POWER_COMPONENT
37
ACPI_MODULE_NAME                ("acpi_power")
38
 
39
#define PREFIX                  "ACPI: "
40
 
41
 
42
int acpi_power_add (struct acpi_device *device);
43
int acpi_power_remove (struct acpi_device *device, int type);
44
 
45
static struct acpi_driver acpi_power_driver = {
46
        .name =         ACPI_POWER_DRIVER_NAME,
47
        .class =        ACPI_POWER_CLASS,
48
        .ids =          ACPI_POWER_HID,
49
        .ops =          {
50
                                .add =          acpi_power_add,
51
                                .remove =       acpi_power_remove,
52
                        },
53
};
54
 
55
struct acpi_power_resource
56
{
57
        acpi_handle             handle;
58
        acpi_bus_id             name;
59
        u32                     system_level;
60
        u32                     order;
61
        int                     state;
62
        int                     references;
63
};
64
 
65
static struct list_head         acpi_power_resource_list;
66
 
67
 
68
/* --------------------------------------------------------------------------
69
                             Power Resource Management
70
   -------------------------------------------------------------------------- */
71
 
72
static int
73
acpi_power_get_context (
74
        acpi_handle             handle,
75
        struct acpi_power_resource **resource)
76
{
77
        int                     result = 0;
78
        struct acpi_device      *device = NULL;
79
 
80
        ACPI_FUNCTION_TRACE("acpi_power_get_context");
81
 
82
        if (!resource)
83
                return_VALUE(-ENODEV);
84
 
85
        result = acpi_bus_get_device(handle, &device);
86
        if (result) {
87
                ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n",
88
                        handle));
89
                return_VALUE(result);
90
        }
91
 
92
        *resource = (struct acpi_power_resource *) acpi_driver_data(device);
93
        if (!resource)
94
                return_VALUE(-ENODEV);
95
 
96
        return_VALUE(0);
97
}
98
 
99
 
100
static int
101
acpi_power_get_state (
102
        struct acpi_power_resource *resource)
103
{
104
        acpi_status             status = AE_OK;
105
        unsigned long           sta = 0;
106
 
107
        ACPI_FUNCTION_TRACE("acpi_power_get_state");
108
 
109
        if (!resource)
110
                return_VALUE(-EINVAL);
111
 
112
        status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta);
113
        if (ACPI_FAILURE(status))
114
                return_VALUE(-ENODEV);
115
 
116
        if (sta & 0x01)
117
                resource->state = ACPI_POWER_RESOURCE_STATE_ON;
118
        else
119
                resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
120
 
121
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
122
                resource->name, resource->state?"on":"off"));
123
 
124
        return_VALUE(0);
125
}
126
 
127
 
128
static int
129
acpi_power_get_list_state (
130
        struct acpi_handle_list *list,
131
        int                     *state)
132
{
133
        int                     result = 0;
134
        struct acpi_power_resource *resource = NULL;
135
        u32                     i = 0;
136
 
137
        ACPI_FUNCTION_TRACE("acpi_power_get_list_state");
138
 
139
        if (!list || !state)
140
                return_VALUE(-EINVAL);
141
 
142
        /* The state of the list is 'on' IFF all resources are 'on'. */
143
 
144
        for (i=0; i<list->count; i++) {
145
                result = acpi_power_get_context(list->handles[i], &resource);
146
                if (result)
147
                        return_VALUE(result);
148
                result = acpi_power_get_state(resource);
149
                if (result)
150
                        return_VALUE(result);
151
 
152
                *state = resource->state;
153
 
154
                if (*state != ACPI_POWER_RESOURCE_STATE_ON)
155
                        break;
156
        }
157
 
158
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
159
                *state?"on":"off"));
160
 
161
        return_VALUE(result);
162
}
163
 
164
 
165
static int
166
acpi_power_on (
167
        acpi_handle             handle)
168
{
169
        int                     result = 0;
170
        acpi_status             status = AE_OK;
171
        struct acpi_device      *device = NULL;
172
        struct acpi_power_resource *resource = NULL;
173
 
174
        ACPI_FUNCTION_TRACE("acpi_power_on");
175
 
176
        result = acpi_power_get_context(handle, &resource);
177
        if (result)
178
                return_VALUE(result);
179
 
180
        resource->references++;
181
 
182
        if ((resource->references > 1)
183
                || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
184
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
185
                        resource->name));
186
                return_VALUE(0);
187
        }
188
 
189
        status = acpi_evaluate_object(resource->handle, "_ON", NULL, NULL);
190
        if (ACPI_FAILURE(status))
191
                return_VALUE(-ENODEV);
192
 
193
        result = acpi_power_get_state(resource);
194
        if (result)
195
                return_VALUE(result);
196
        if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
197
                return_VALUE(-ENOEXEC);
198
 
199
        /* Update the power resource's _device_ power state */
200
        result = acpi_bus_get_device(resource->handle, &device);
201
        if (result)
202
                return_VALUE(result);
203
        device->power.state = ACPI_STATE_D0;
204
 
205
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
206
                resource->name));
207
 
208
        return_VALUE(0);
209
}
210
 
211
 
212
static int
213
acpi_power_off_device (
214
        acpi_handle             handle)
215
{
216
        int                     result = 0;
217
        acpi_status             status = AE_OK;
218
        struct acpi_device      *device = NULL;
219
        struct acpi_power_resource *resource = NULL;
220
 
221
        ACPI_FUNCTION_TRACE("acpi_power_off_device");
222
 
223
        result = acpi_power_get_context(handle, &resource);
224
        if (result)
225
                return_VALUE(result);
226
 
227
        if (resource->references)
228
                resource->references--;
229
 
230
        if (resource->references) {
231
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
232
                        "Resource [%s] is still in use, dereferencing\n",
233
                        device->pnp.bus_id));
234
                return_VALUE(0);
235
        }
236
 
237
        if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
238
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
239
                        device->pnp.bus_id));
240
                return_VALUE(0);
241
        }
242
 
243
        status = acpi_evaluate_object(resource->handle, "_OFF", NULL, NULL);
244
        if (ACPI_FAILURE(status))
245
                return_VALUE(-ENODEV);
246
 
247
        result = acpi_power_get_state(resource);
248
        if (result)
249
                return_VALUE(result);
250
        if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
251
                return_VALUE(-ENOEXEC);
252
 
253
        /* Update the power resource's _device_ power state */
254
        result = acpi_bus_get_device(resource->handle, &device);
255
        if (result)
256
                return_VALUE(result);
257
        device->power.state = ACPI_STATE_D3;
258
 
259
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
260
                resource->name));
261
 
262
        return_VALUE(0);
263
}
264
 
265
 
266
/* --------------------------------------------------------------------------
267
                             Device Power Management
268
   -------------------------------------------------------------------------- */
269
 
270
int
271
acpi_power_get_inferred_state (
272
        struct acpi_device      *device)
273
{
274
        int                     result = 0;
275
        struct acpi_handle_list *list = NULL;
276
        int                     list_state = 0;
277
        int                     i = 0;
278
 
279
        ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state");
280
 
281
        if (!device)
282
                return_VALUE(-EINVAL);
283
 
284
        device->power.state = ACPI_STATE_UNKNOWN;
285
 
286
        /*
287
         * We know a device's inferred power state when all the resources
288
         * required for a given D-state are 'on'.
289
         */
290
        for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
291
                list = &device->power.states[i].resources;
292
                if (list->count < 1)
293
                        continue;
294
 
295
                result = acpi_power_get_list_state(list, &list_state);
296
                if (result)
297
                        return_VALUE(result);
298
 
299
                if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
300
                        device->power.state = i;
301
                        return_VALUE(0);
302
                }
303
        }
304
 
305
        device->power.state = ACPI_STATE_D3;
306
 
307
        return_VALUE(0);
308
}
309
 
310
 
311
int
312
acpi_power_transition (
313
        struct acpi_device      *device,
314
        int                     state)
315
{
316
        int                     result = 0;
317
        struct acpi_handle_list *cl = NULL;     /* Current Resources */
318
        struct acpi_handle_list *tl = NULL;     /* Target Resources */
319
        int                     i = 0;
320
 
321
        ACPI_FUNCTION_TRACE("acpi_power_transition");
322
 
323
        if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
324
                return_VALUE(-EINVAL);
325
 
326
        if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3))
327
                return_VALUE(-ENODEV);
328
 
329
        cl = &device->power.states[device->power.state].resources;
330
        tl = &device->power.states[state].resources;
331
 
332
        device->power.state = ACPI_STATE_UNKNOWN;
333
 
334
        if (!cl->count && !tl->count) {
335
                result = -ENODEV;
336
                goto end;
337
        }
338
 
339
        /* TBD: Resources must be ordered. */
340
 
341
        /*
342
         * First we reference all power resources required in the target list
343
         * (e.g. so the device doesn't loose power while transitioning).
344
         */
345
        for (i=0; i<tl->count; i++) {
346
                result = acpi_power_on(tl->handles[i]);
347
                if (result)
348
                        goto end;
349
        }
350
 
351
        /*
352
         * Then we dereference all power resources used in the current list.
353
         */
354
        for (i=0; i<cl->count; i++) {
355
                result = acpi_power_off_device(cl->handles[i]);
356
                if (result)
357
                        goto end;
358
        }
359
 
360
        /* We shouldn't change the state till all above operations succeed */
361
        device->power.state = state;
362
end:
363
        if (result)
364
                ACPI_DEBUG_PRINT((ACPI_DB_WARN,
365
                        "Error transitioning device [%s] to D%d\n",
366
                        device->pnp.bus_id, state));
367
 
368
        return_VALUE(result);
369
}
370
 
371
 
372
/* --------------------------------------------------------------------------
373
                              FS Interface (/proc)
374
   -------------------------------------------------------------------------- */
375
 
376
struct proc_dir_entry           *acpi_power_dir;
377
 
378
 
379
static int
380
acpi_power_read_status (
381
        char                    *page,
382
        char                    **start,
383
        off_t                   off,
384
        int                     count,
385
        int                     *eof,
386
        void                    *data)
387
{
388
        struct acpi_power_resource *resource = NULL;
389
        char                    *p = page;
390
        int                     len;
391
 
392
        ACPI_FUNCTION_TRACE("acpi_power_read_status");
393
 
394
        if (!data || (off != 0))
395
                goto end;
396
 
397
        resource = (struct acpi_power_resource *) data;
398
 
399
        p += sprintf(p, "state:                   ");
400
        switch (resource->state) {
401
        case ACPI_POWER_RESOURCE_STATE_ON:
402
                p += sprintf(p, "on\n");
403
                break;
404
        case ACPI_POWER_RESOURCE_STATE_OFF:
405
                p += sprintf(p, "off\n");
406
                break;
407
        default:
408
                p += sprintf(p, "unknown\n");
409
                break;
410
        }
411
 
412
        p += sprintf(p, "system level:            S%d\n",
413
                resource->system_level);
414
        p += sprintf(p, "order:                   %d\n",
415
                resource->order);
416
        p += sprintf(p, "reference count:         %d\n",
417
                resource->references);
418
 
419
end:
420
        len = (p - page);
421
        if (len <= off+count) *eof = 1;
422
        *start = page + off;
423
        len -= off;
424
        if (len>count) len = count;
425
        if (len<0) len = 0;
426
 
427
        return_VALUE(len);
428
}
429
 
430
 
431
static int
432
acpi_power_add_fs (
433
        struct acpi_device      *device)
434
{
435
        struct proc_dir_entry   *entry = NULL;
436
 
437
        ACPI_FUNCTION_TRACE("acpi_power_add_fs");
438
 
439
        if (!device)
440
                return_VALUE(-EINVAL);
441
 
442
        if (!acpi_device_dir(device)) {
443
                acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
444
                        acpi_power_dir);
445
                if (!acpi_device_dir(device))
446
                        return_VALUE(-ENODEV);
447
        }
448
 
449
        /* 'status' [R] */
450
        entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
451
                S_IRUGO, acpi_device_dir(device));
452
        if (!entry)
453
                ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
454
                        "Unable to create '%s' fs entry\n",
455
                        ACPI_POWER_FILE_STATUS));
456
        else {
457
                entry->read_proc = acpi_power_read_status;
458
                entry->data = acpi_driver_data(device);
459
        }
460
 
461
        return_VALUE(0);
462
}
463
 
464
 
465
static int
466
acpi_power_remove_fs (
467
        struct acpi_device      *device)
468
{
469
        ACPI_FUNCTION_TRACE("acpi_power_remove_fs");
470
 
471
        if (acpi_device_dir(device)) {
472
                remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
473
                acpi_device_dir(device) = NULL;
474
        }
475
 
476
        return_VALUE(0);
477
}
478
 
479
 
480
/* --------------------------------------------------------------------------
481
                                Driver Interface
482
   -------------------------------------------------------------------------- */
483
 
484
int
485
acpi_power_add (
486
        struct acpi_device      *device)
487
{
488
        int                     result = 0;
489
        acpi_status             status = AE_OK;
490
        struct acpi_power_resource *resource = NULL;
491
        union acpi_object       acpi_object;
492
        struct acpi_buffer      buffer = {sizeof(acpi_object), &acpi_object};
493
 
494
        ACPI_FUNCTION_TRACE("acpi_power_add");
495
 
496
        if (!device)
497
                return_VALUE(-EINVAL);
498
 
499
        resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
500
        if (!resource)
501
                return_VALUE(-ENOMEM);
502
        memset(resource, 0, sizeof(struct acpi_power_resource));
503
 
504
        resource->handle = device->handle;
505
        sprintf(resource->name, "%s", device->pnp.bus_id);
506
        sprintf(acpi_device_name(device), "%s", ACPI_POWER_DEVICE_NAME);
507
        sprintf(acpi_device_class(device), "%s", ACPI_POWER_CLASS);
508
        acpi_driver_data(device) = resource;
509
 
510
        /* Evalute the object to get the system level and resource order. */
511
        status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer);
512
        if (ACPI_FAILURE(status)) {
513
                result = -ENODEV;
514
                goto end;
515
        }
516
        resource->system_level = acpi_object.power_resource.system_level;
517
        resource->order = acpi_object.power_resource.resource_order;
518
 
519
        result = acpi_power_get_state(resource);
520
        if (result)
521
                goto end;
522
 
523
        switch (resource->state) {
524
        case ACPI_POWER_RESOURCE_STATE_ON:
525
                device->power.state = ACPI_STATE_D0;
526
                break;
527
        case ACPI_POWER_RESOURCE_STATE_OFF:
528
                device->power.state = ACPI_STATE_D3;
529
                break;
530
        default:
531
                device->power.state = ACPI_STATE_UNKNOWN;
532
                break;
533
        }
534
 
535
        result = acpi_power_add_fs(device);
536
        if (result)
537
                goto end;
538
 
539
        printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
540
                acpi_device_bid(device), resource->state?"on":"off");
541
 
542
end:
543
        if (result)
544
                kfree(resource);
545
 
546
        return_VALUE(result);
547
}
548
 
549
 
550
int
551
acpi_power_remove (
552
        struct acpi_device      *device,
553
        int                     type)
554
{
555
        struct acpi_power_resource *resource = NULL;
556
 
557
        ACPI_FUNCTION_TRACE("acpi_power_remove");
558
 
559
        if (!device || !acpi_driver_data(device))
560
                return_VALUE(-EINVAL);
561
 
562
        resource = (struct acpi_power_resource *) acpi_driver_data(device);
563
 
564
        acpi_power_remove_fs(device);
565
 
566
        kfree(resource);
567
 
568
        return_VALUE(0);
569
}
570
 
571
 
572
int __init
573
acpi_power_init (void)
574
{
575
        int                     result = 0;
576
 
577
        ACPI_FUNCTION_TRACE("acpi_power_init");
578
 
579
        INIT_LIST_HEAD(&acpi_power_resource_list);
580
 
581
        acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
582
        if (!acpi_power_dir)
583
                return_VALUE(-ENODEV);
584
 
585
        result = acpi_bus_register_driver(&acpi_power_driver);
586
        if (result < 0) {
587
                remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
588
                return_VALUE(-ENODEV);
589
        }
590
 
591
        return_VALUE(0);
592
}
593
 
594
 
595
void __exit
596
acpi_power_exit (void)
597
{
598
        ACPI_FUNCTION_TRACE("acpi_power_exit");
599
 
600
        /* TBD: Empty acpi_power_resource_list */
601
 
602
        acpi_bus_unregister_driver(&acpi_power_driver);
603
 
604
        remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
605
 
606
        return_VOID;
607
}

powered by: WebSVN 2.1.0

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