Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) | |
3 | * | |
4 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | |
5 | * | |
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or (at | |
11 | * your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | |
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
21 | * | |
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
23 | */ | |
24 | ||
25 | #include <linux/module.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/ioport.h> | |
28 | #include <linux/list.h> | |
29 | #include <linux/sched.h> | |
30 | #include <linux/pm.h> | |
31 | #include <linux/device.h> | |
32 | #include <linux/proc_fs.h> | |
33 | #ifdef CONFIG_X86 | |
34 | #include <asm/mpspec.h> | |
35 | #endif | |
36 | #include <acpi/acpi_bus.h> | |
37 | #include <acpi/acpi_drivers.h> | |
38 | ||
1da177e4 | 39 | #define _COMPONENT ACPI_BUS_COMPONENT |
4be44fcd | 40 | ACPI_MODULE_NAME("acpi_bus") |
1da177e4 LT |
41 | #ifdef CONFIG_X86 |
42 | extern void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger); | |
43 | #endif | |
44 | ||
4be44fcd | 45 | FADT_DESCRIPTOR acpi_fadt; |
1da177e4 LT |
46 | EXPORT_SYMBOL(acpi_fadt); |
47 | ||
4be44fcd LB |
48 | struct acpi_device *acpi_root; |
49 | struct proc_dir_entry *acpi_root_dir; | |
1da177e4 LT |
50 | EXPORT_SYMBOL(acpi_root_dir); |
51 | ||
52 | #define STRUCT_TO_INT(s) (*((int*)&s)) | |
53 | ||
54 | /* -------------------------------------------------------------------------- | |
55 | Device Management | |
56 | -------------------------------------------------------------------------- */ | |
57 | ||
4be44fcd | 58 | int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) |
1da177e4 | 59 | { |
4be44fcd | 60 | acpi_status status = AE_OK; |
1da177e4 LT |
61 | |
62 | ACPI_FUNCTION_TRACE("acpi_bus_get_device"); | |
63 | ||
64 | if (!device) | |
65 | return_VALUE(-EINVAL); | |
66 | ||
67 | /* TBD: Support fixed-feature devices */ | |
68 | ||
4be44fcd | 69 | status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device); |
1da177e4 LT |
70 | if (ACPI_FAILURE(status) || !*device) { |
71 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "No context for object [%p]\n", | |
4be44fcd | 72 | handle)); |
1da177e4 LT |
73 | return_VALUE(-ENODEV); |
74 | } | |
75 | ||
76 | return_VALUE(0); | |
77 | } | |
4be44fcd | 78 | |
1da177e4 LT |
79 | EXPORT_SYMBOL(acpi_bus_get_device); |
80 | ||
4be44fcd | 81 | int acpi_bus_get_status(struct acpi_device *device) |
1da177e4 | 82 | { |
4be44fcd LB |
83 | acpi_status status = AE_OK; |
84 | unsigned long sta = 0; | |
85 | ||
1da177e4 LT |
86 | ACPI_FUNCTION_TRACE("acpi_bus_get_status"); |
87 | ||
88 | if (!device) | |
89 | return_VALUE(-EINVAL); | |
90 | ||
91 | /* | |
92 | * Evaluate _STA if present. | |
93 | */ | |
94 | if (device->flags.dynamic_status) { | |
4be44fcd LB |
95 | status = |
96 | acpi_evaluate_integer(device->handle, "_STA", NULL, &sta); | |
1da177e4 LT |
97 | if (ACPI_FAILURE(status)) |
98 | return_VALUE(-ENODEV); | |
4be44fcd | 99 | STRUCT_TO_INT(device->status) = (int)sta; |
1da177e4 LT |
100 | } |
101 | ||
102 | /* | |
103 | * Otherwise we assume the status of our parent (unless we don't | |
104 | * have one, in which case status is implied). | |
105 | */ | |
106 | else if (device->parent) | |
107 | device->status = device->parent->status; | |
108 | else | |
109 | STRUCT_TO_INT(device->status) = 0x0F; | |
110 | ||
111 | if (device->status.functional && !device->status.present) { | |
112 | printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: " | |
4be44fcd LB |
113 | "functional but not present; setting present\n", |
114 | device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status)); | |
1da177e4 LT |
115 | device->status.present = 1; |
116 | } | |
117 | ||
4be44fcd LB |
118 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", |
119 | device->pnp.bus_id, | |
120 | (u32) STRUCT_TO_INT(device->status))); | |
1da177e4 LT |
121 | |
122 | return_VALUE(0); | |
123 | } | |
1da177e4 | 124 | |
4be44fcd | 125 | EXPORT_SYMBOL(acpi_bus_get_status); |
1da177e4 LT |
126 | |
127 | /* -------------------------------------------------------------------------- | |
128 | Power Management | |
129 | -------------------------------------------------------------------------- */ | |
130 | ||
4be44fcd | 131 | int acpi_bus_get_power(acpi_handle handle, int *state) |
1da177e4 | 132 | { |
4be44fcd LB |
133 | int result = 0; |
134 | acpi_status status = 0; | |
135 | struct acpi_device *device = NULL; | |
136 | unsigned long psc = 0; | |
1da177e4 LT |
137 | |
138 | ACPI_FUNCTION_TRACE("acpi_bus_get_power"); | |
139 | ||
140 | result = acpi_bus_get_device(handle, &device); | |
141 | if (result) | |
142 | return_VALUE(result); | |
143 | ||
144 | *state = ACPI_STATE_UNKNOWN; | |
145 | ||
146 | if (!device->flags.power_manageable) { | |
147 | /* TBD: Non-recursive algorithm for walking up hierarchy */ | |
148 | if (device->parent) | |
149 | *state = device->parent->power.state; | |
150 | else | |
151 | *state = ACPI_STATE_D0; | |
4be44fcd | 152 | } else { |
1da177e4 LT |
153 | /* |
154 | * Get the device's power state either directly (via _PSC) or | |
155 | * indirectly (via power resources). | |
156 | */ | |
157 | if (device->power.flags.explicit_get) { | |
4be44fcd LB |
158 | status = acpi_evaluate_integer(device->handle, "_PSC", |
159 | NULL, &psc); | |
1da177e4 LT |
160 | if (ACPI_FAILURE(status)) |
161 | return_VALUE(-ENODEV); | |
4be44fcd LB |
162 | device->power.state = (int)psc; |
163 | } else if (device->power.flags.power_resources) { | |
1da177e4 LT |
164 | result = acpi_power_get_inferred_state(device); |
165 | if (result) | |
166 | return_VALUE(result); | |
167 | } | |
168 | ||
169 | *state = device->power.state; | |
170 | } | |
171 | ||
172 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", | |
4be44fcd | 173 | device->pnp.bus_id, device->power.state)); |
1da177e4 LT |
174 | |
175 | return_VALUE(0); | |
176 | } | |
1da177e4 | 177 | |
4be44fcd | 178 | EXPORT_SYMBOL(acpi_bus_get_power); |
1da177e4 | 179 | |
4be44fcd | 180 | int acpi_bus_set_power(acpi_handle handle, int state) |
1da177e4 | 181 | { |
4be44fcd LB |
182 | int result = 0; |
183 | acpi_status status = AE_OK; | |
184 | struct acpi_device *device = NULL; | |
185 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; | |
1da177e4 LT |
186 | |
187 | ACPI_FUNCTION_TRACE("acpi_bus_set_power"); | |
188 | ||
189 | result = acpi_bus_get_device(handle, &device); | |
190 | if (result) | |
191 | return_VALUE(result); | |
192 | ||
193 | if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) | |
194 | return_VALUE(-EINVAL); | |
195 | ||
196 | /* Make sure this is a valid target state */ | |
197 | ||
198 | if (!device->flags.power_manageable) { | |
4be44fcd LB |
199 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, |
200 | "Device is not power manageable\n")); | |
1da177e4 LT |
201 | return_VALUE(-ENODEV); |
202 | } | |
b913100d DSL |
203 | /* |
204 | * Get device's current power state if it's unknown | |
205 | * This means device power state isn't initialized or previous setting failed | |
206 | */ | |
207 | if (device->power.state == ACPI_STATE_UNKNOWN) | |
208 | acpi_bus_get_power(device->handle, &device->power.state); | |
1da177e4 | 209 | if (state == device->power.state) { |
4be44fcd LB |
210 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", |
211 | state)); | |
1da177e4 LT |
212 | return_VALUE(0); |
213 | } | |
214 | if (!device->power.states[state].flags.valid) { | |
4be44fcd LB |
215 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device does not support D%d\n", |
216 | state)); | |
1da177e4 LT |
217 | return_VALUE(-ENODEV); |
218 | } | |
219 | if (device->parent && (state < device->parent->power.state)) { | |
4be44fcd LB |
220 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, |
221 | "Cannot set device to a higher-powered state than parent\n")); | |
1da177e4 LT |
222 | return_VALUE(-ENODEV); |
223 | } | |
224 | ||
225 | /* | |
226 | * Transition Power | |
227 | * ---------------- | |
228 | * On transitions to a high-powered state we first apply power (via | |
229 | * power resources) then evalute _PSx. Conversly for transitions to | |
230 | * a lower-powered state. | |
b913100d | 231 | */ |
1da177e4 LT |
232 | if (state < device->power.state) { |
233 | if (device->power.flags.power_resources) { | |
234 | result = acpi_power_transition(device, state); | |
235 | if (result) | |
236 | goto end; | |
237 | } | |
238 | if (device->power.states[state].flags.explicit_set) { | |
4be44fcd LB |
239 | status = acpi_evaluate_object(device->handle, |
240 | object_name, NULL, NULL); | |
1da177e4 LT |
241 | if (ACPI_FAILURE(status)) { |
242 | result = -ENODEV; | |
243 | goto end; | |
244 | } | |
245 | } | |
4be44fcd | 246 | } else { |
1da177e4 | 247 | if (device->power.states[state].flags.explicit_set) { |
4be44fcd LB |
248 | status = acpi_evaluate_object(device->handle, |
249 | object_name, NULL, NULL); | |
1da177e4 LT |
250 | if (ACPI_FAILURE(status)) { |
251 | result = -ENODEV; | |
252 | goto end; | |
253 | } | |
254 | } | |
255 | if (device->power.flags.power_resources) { | |
256 | result = acpi_power_transition(device, state); | |
257 | if (result) | |
258 | goto end; | |
259 | } | |
260 | } | |
261 | ||
4be44fcd | 262 | end: |
1da177e4 | 263 | if (result) |
4be44fcd LB |
264 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, |
265 | "Error transitioning device [%s] to D%d\n", | |
266 | device->pnp.bus_id, state)); | |
1da177e4 | 267 | else |
4be44fcd LB |
268 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
269 | "Device [%s] transitioned to D%d\n", | |
270 | device->pnp.bus_id, state)); | |
1da177e4 LT |
271 | |
272 | return_VALUE(result); | |
273 | } | |
1da177e4 | 274 | |
4be44fcd | 275 | EXPORT_SYMBOL(acpi_bus_set_power); |
1da177e4 LT |
276 | |
277 | /* -------------------------------------------------------------------------- | |
278 | Event Management | |
279 | -------------------------------------------------------------------------- */ | |
280 | ||
281 | static DEFINE_SPINLOCK(acpi_bus_event_lock); | |
282 | ||
283 | LIST_HEAD(acpi_bus_event_list); | |
284 | DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue); | |
285 | ||
4be44fcd | 286 | extern int event_is_open; |
1da177e4 | 287 | |
4be44fcd | 288 | int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data) |
1da177e4 | 289 | { |
4be44fcd LB |
290 | struct acpi_bus_event *event = NULL; |
291 | unsigned long flags = 0; | |
1da177e4 LT |
292 | |
293 | ACPI_FUNCTION_TRACE("acpi_bus_generate_event"); | |
294 | ||
295 | if (!device) | |
296 | return_VALUE(-EINVAL); | |
297 | ||
298 | /* drop event on the floor if no one's listening */ | |
299 | if (!event_is_open) | |
300 | return_VALUE(0); | |
301 | ||
302 | event = kmalloc(sizeof(struct acpi_bus_event), GFP_ATOMIC); | |
303 | if (!event) | |
304 | return_VALUE(-ENOMEM); | |
305 | ||
306 | strcpy(event->device_class, device->pnp.device_class); | |
307 | strcpy(event->bus_id, device->pnp.bus_id); | |
308 | event->type = type; | |
309 | event->data = data; | |
310 | ||
311 | spin_lock_irqsave(&acpi_bus_event_lock, flags); | |
312 | list_add_tail(&event->node, &acpi_bus_event_list); | |
313 | spin_unlock_irqrestore(&acpi_bus_event_lock, flags); | |
314 | ||
315 | wake_up_interruptible(&acpi_bus_event_queue); | |
316 | ||
317 | return_VALUE(0); | |
318 | } | |
4be44fcd | 319 | |
1da177e4 LT |
320 | EXPORT_SYMBOL(acpi_bus_generate_event); |
321 | ||
4be44fcd | 322 | int acpi_bus_receive_event(struct acpi_bus_event *event) |
1da177e4 | 323 | { |
4be44fcd LB |
324 | unsigned long flags = 0; |
325 | struct acpi_bus_event *entry = NULL; | |
1da177e4 LT |
326 | |
327 | DECLARE_WAITQUEUE(wait, current); | |
328 | ||
329 | ACPI_FUNCTION_TRACE("acpi_bus_receive_event"); | |
330 | ||
331 | if (!event) | |
332 | return_VALUE(-EINVAL); | |
333 | ||
334 | if (list_empty(&acpi_bus_event_list)) { | |
335 | ||
336 | set_current_state(TASK_INTERRUPTIBLE); | |
337 | add_wait_queue(&acpi_bus_event_queue, &wait); | |
338 | ||
339 | if (list_empty(&acpi_bus_event_list)) | |
340 | schedule(); | |
341 | ||
342 | remove_wait_queue(&acpi_bus_event_queue, &wait); | |
343 | set_current_state(TASK_RUNNING); | |
344 | ||
345 | if (signal_pending(current)) | |
346 | return_VALUE(-ERESTARTSYS); | |
347 | } | |
348 | ||
349 | spin_lock_irqsave(&acpi_bus_event_lock, flags); | |
4be44fcd LB |
350 | entry = |
351 | list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node); | |
1da177e4 LT |
352 | if (entry) |
353 | list_del(&entry->node); | |
354 | spin_unlock_irqrestore(&acpi_bus_event_lock, flags); | |
355 | ||
356 | if (!entry) | |
357 | return_VALUE(-ENODEV); | |
358 | ||
359 | memcpy(event, entry, sizeof(struct acpi_bus_event)); | |
360 | ||
361 | kfree(entry); | |
362 | ||
363 | return_VALUE(0); | |
364 | } | |
1da177e4 | 365 | |
4be44fcd | 366 | EXPORT_SYMBOL(acpi_bus_receive_event); |
1da177e4 LT |
367 | |
368 | /* -------------------------------------------------------------------------- | |
369 | Notification Handling | |
370 | -------------------------------------------------------------------------- */ | |
371 | ||
372 | static int | |
4be44fcd | 373 | acpi_bus_check_device(struct acpi_device *device, int *status_changed) |
1da177e4 | 374 | { |
4be44fcd | 375 | acpi_status status = 0; |
1da177e4 LT |
376 | struct acpi_device_status old_status; |
377 | ||
378 | ACPI_FUNCTION_TRACE("acpi_bus_check_device"); | |
379 | ||
380 | if (!device) | |
381 | return_VALUE(-EINVAL); | |
382 | ||
383 | if (status_changed) | |
384 | *status_changed = 0; | |
385 | ||
386 | old_status = device->status; | |
387 | ||
388 | /* | |
389 | * Make sure this device's parent is present before we go about | |
390 | * messing with the device. | |
391 | */ | |
392 | if (device->parent && !device->parent->status.present) { | |
393 | device->status = device->parent->status; | |
394 | if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) { | |
395 | if (status_changed) | |
396 | *status_changed = 1; | |
397 | } | |
398 | return_VALUE(0); | |
399 | } | |
400 | ||
401 | status = acpi_bus_get_status(device); | |
402 | if (ACPI_FAILURE(status)) | |
403 | return_VALUE(-ENODEV); | |
404 | ||
405 | if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status)) | |
406 | return_VALUE(0); | |
407 | ||
408 | if (status_changed) | |
409 | *status_changed = 1; | |
4be44fcd | 410 | |
1da177e4 LT |
411 | /* |
412 | * Device Insertion/Removal | |
413 | */ | |
414 | if ((device->status.present) && !(old_status.present)) { | |
415 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n")); | |
416 | /* TBD: Handle device insertion */ | |
4be44fcd | 417 | } else if (!(device->status.present) && (old_status.present)) { |
1da177e4 LT |
418 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n")); |
419 | /* TBD: Handle device removal */ | |
420 | } | |
421 | ||
422 | return_VALUE(0); | |
423 | } | |
424 | ||
4be44fcd | 425 | static int acpi_bus_check_scope(struct acpi_device *device) |
1da177e4 | 426 | { |
4be44fcd LB |
427 | int result = 0; |
428 | int status_changed = 0; | |
1da177e4 LT |
429 | |
430 | ACPI_FUNCTION_TRACE("acpi_bus_check_scope"); | |
431 | ||
432 | if (!device) | |
433 | return_VALUE(-EINVAL); | |
434 | ||
435 | /* Status Change? */ | |
436 | result = acpi_bus_check_device(device, &status_changed); | |
437 | if (result) | |
438 | return_VALUE(result); | |
439 | ||
440 | if (!status_changed) | |
441 | return_VALUE(0); | |
442 | ||
443 | /* | |
444 | * TBD: Enumerate child devices within this device's scope and | |
445 | * run acpi_bus_check_device()'s on them. | |
446 | */ | |
447 | ||
448 | return_VALUE(0); | |
449 | } | |
450 | ||
1da177e4 LT |
451 | /** |
452 | * acpi_bus_notify | |
453 | * --------------- | |
454 | * Callback for all 'system-level' device notifications (values 0x00-0x7F). | |
455 | */ | |
4be44fcd | 456 | static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) |
1da177e4 | 457 | { |
4be44fcd LB |
458 | int result = 0; |
459 | struct acpi_device *device = NULL; | |
1da177e4 LT |
460 | |
461 | ACPI_FUNCTION_TRACE("acpi_bus_notify"); | |
462 | ||
463 | if (acpi_bus_get_device(handle, &device)) | |
464 | return_VOID; | |
465 | ||
466 | switch (type) { | |
467 | ||
468 | case ACPI_NOTIFY_BUS_CHECK: | |
4be44fcd LB |
469 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
470 | "Received BUS CHECK notification for device [%s]\n", | |
471 | device->pnp.bus_id)); | |
1da177e4 LT |
472 | result = acpi_bus_check_scope(device); |
473 | /* | |
474 | * TBD: We'll need to outsource certain events to non-ACPI | |
4be44fcd | 475 | * drivers via the device manager (device.c). |
1da177e4 LT |
476 | */ |
477 | break; | |
478 | ||
479 | case ACPI_NOTIFY_DEVICE_CHECK: | |
4be44fcd LB |
480 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
481 | "Received DEVICE CHECK notification for device [%s]\n", | |
482 | device->pnp.bus_id)); | |
1da177e4 LT |
483 | result = acpi_bus_check_device(device, NULL); |
484 | /* | |
485 | * TBD: We'll need to outsource certain events to non-ACPI | |
4be44fcd | 486 | * drivers via the device manager (device.c). |
1da177e4 LT |
487 | */ |
488 | break; | |
489 | ||
490 | case ACPI_NOTIFY_DEVICE_WAKE: | |
4be44fcd LB |
491 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
492 | "Received DEVICE WAKE notification for device [%s]\n", | |
493 | device->pnp.bus_id)); | |
1da177e4 LT |
494 | /* TBD */ |
495 | break; | |
496 | ||
497 | case ACPI_NOTIFY_EJECT_REQUEST: | |
4be44fcd LB |
498 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
499 | "Received EJECT REQUEST notification for device [%s]\n", | |
500 | device->pnp.bus_id)); | |
1da177e4 LT |
501 | /* TBD */ |
502 | break; | |
503 | ||
504 | case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: | |
4be44fcd LB |
505 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
506 | "Received DEVICE CHECK LIGHT notification for device [%s]\n", | |
507 | device->pnp.bus_id)); | |
1da177e4 LT |
508 | /* TBD: Exactly what does 'light' mean? */ |
509 | break; | |
510 | ||
511 | case ACPI_NOTIFY_FREQUENCY_MISMATCH: | |
4be44fcd LB |
512 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
513 | "Received FREQUENCY MISMATCH notification for device [%s]\n", | |
514 | device->pnp.bus_id)); | |
1da177e4 LT |
515 | /* TBD */ |
516 | break; | |
517 | ||
518 | case ACPI_NOTIFY_BUS_MODE_MISMATCH: | |
4be44fcd LB |
519 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
520 | "Received BUS MODE MISMATCH notification for device [%s]\n", | |
521 | device->pnp.bus_id)); | |
1da177e4 LT |
522 | /* TBD */ |
523 | break; | |
524 | ||
525 | case ACPI_NOTIFY_POWER_FAULT: | |
4be44fcd LB |
526 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
527 | "Received POWER FAULT notification for device [%s]\n", | |
528 | device->pnp.bus_id)); | |
1da177e4 LT |
529 | /* TBD */ |
530 | break; | |
531 | ||
532 | default: | |
4be44fcd LB |
533 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
534 | "Received unknown/unsupported notification [%08x]\n", | |
535 | type)); | |
1da177e4 LT |
536 | break; |
537 | } | |
538 | ||
539 | return_VOID; | |
540 | } | |
541 | ||
542 | /* -------------------------------------------------------------------------- | |
543 | Initialization/Cleanup | |
544 | -------------------------------------------------------------------------- */ | |
545 | ||
4be44fcd | 546 | static int __init acpi_bus_init_irq(void) |
1da177e4 | 547 | { |
4be44fcd LB |
548 | acpi_status status = AE_OK; |
549 | union acpi_object arg = { ACPI_TYPE_INTEGER }; | |
550 | struct acpi_object_list arg_list = { 1, &arg }; | |
551 | char *message = NULL; | |
1da177e4 LT |
552 | |
553 | ACPI_FUNCTION_TRACE("acpi_bus_init_irq"); | |
554 | ||
555 | /* | |
556 | * Let the system know what interrupt model we are using by | |
557 | * evaluating the \_PIC object, if exists. | |
558 | */ | |
559 | ||
560 | switch (acpi_irq_model) { | |
561 | case ACPI_IRQ_MODEL_PIC: | |
562 | message = "PIC"; | |
563 | break; | |
564 | case ACPI_IRQ_MODEL_IOAPIC: | |
565 | message = "IOAPIC"; | |
566 | break; | |
567 | case ACPI_IRQ_MODEL_IOSAPIC: | |
568 | message = "IOSAPIC"; | |
569 | break; | |
570 | default: | |
571 | printk(KERN_WARNING PREFIX "Unknown interrupt routing model\n"); | |
572 | return_VALUE(-ENODEV); | |
573 | } | |
574 | ||
575 | printk(KERN_INFO PREFIX "Using %s for interrupt routing\n", message); | |
576 | ||
577 | arg.integer.value = acpi_irq_model; | |
578 | ||
579 | status = acpi_evaluate_object(NULL, "\\_PIC", &arg_list, NULL); | |
580 | if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { | |
581 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PIC\n")); | |
582 | return_VALUE(-ENODEV); | |
583 | } | |
584 | ||
585 | return_VALUE(0); | |
586 | } | |
587 | ||
4be44fcd | 588 | void __init acpi_early_init(void) |
1da177e4 | 589 | { |
4be44fcd LB |
590 | acpi_status status = AE_OK; |
591 | struct acpi_buffer buffer = { sizeof(acpi_fadt), &acpi_fadt }; | |
1da177e4 LT |
592 | |
593 | ACPI_FUNCTION_TRACE("acpi_early_init"); | |
594 | ||
595 | if (acpi_disabled) | |
596 | return_VOID; | |
597 | ||
598 | /* enable workarounds, unless strict ACPI spec. compliance */ | |
599 | if (!acpi_strict) | |
600 | acpi_gbl_enable_interpreter_slack = TRUE; | |
601 | ||
602 | status = acpi_initialize_subsystem(); | |
603 | if (ACPI_FAILURE(status)) { | |
4be44fcd LB |
604 | printk(KERN_ERR PREFIX |
605 | "Unable to initialize the ACPI Interpreter\n"); | |
1da177e4 LT |
606 | goto error0; |
607 | } | |
608 | ||
609 | status = acpi_load_tables(); | |
610 | if (ACPI_FAILURE(status)) { | |
4be44fcd LB |
611 | printk(KERN_ERR PREFIX |
612 | "Unable to load the System Description Tables\n"); | |
1da177e4 LT |
613 | goto error0; |
614 | } | |
615 | ||
616 | /* | |
617 | * Get a separate copy of the FADT for use by other drivers. | |
618 | */ | |
619 | status = acpi_get_table(ACPI_TABLE_FADT, 1, &buffer); | |
620 | if (ACPI_FAILURE(status)) { | |
621 | printk(KERN_ERR PREFIX "Unable to get the FADT\n"); | |
622 | goto error0; | |
623 | } | |
1da177e4 LT |
624 | #ifdef CONFIG_X86 |
625 | if (!acpi_ioapic) { | |
626 | extern acpi_interrupt_flags acpi_sci_flags; | |
627 | ||
628 | /* compatible (0) means level (3) */ | |
629 | if (acpi_sci_flags.trigger == 0) | |
630 | acpi_sci_flags.trigger = 3; | |
631 | ||
632 | /* Set PIC-mode SCI trigger type */ | |
4be44fcd LB |
633 | acpi_pic_sci_set_trigger(acpi_fadt.sci_int, |
634 | acpi_sci_flags.trigger); | |
1da177e4 LT |
635 | } else { |
636 | extern int acpi_sci_override_gsi; | |
637 | /* | |
638 | * now that acpi_fadt is initialized, | |
639 | * update it with result from INT_SRC_OVR parsing | |
640 | */ | |
641 | acpi_fadt.sci_int = acpi_sci_override_gsi; | |
642 | } | |
643 | #endif | |
644 | ||
4be44fcd LB |
645 | status = |
646 | acpi_enable_subsystem(~ | |
647 | (ACPI_NO_HARDWARE_INIT | | |
648 | ACPI_NO_ACPI_ENABLE)); | |
1da177e4 LT |
649 | if (ACPI_FAILURE(status)) { |
650 | printk(KERN_ERR PREFIX "Unable to enable ACPI\n"); | |
651 | goto error0; | |
652 | } | |
653 | ||
654 | return_VOID; | |
655 | ||
4be44fcd | 656 | error0: |
1da177e4 LT |
657 | disable_acpi(); |
658 | return_VOID; | |
659 | } | |
660 | ||
4be44fcd | 661 | static int __init acpi_bus_init(void) |
1da177e4 | 662 | { |
4be44fcd LB |
663 | int result = 0; |
664 | acpi_status status = AE_OK; | |
665 | extern acpi_status acpi_os_initialize1(void); | |
1da177e4 LT |
666 | |
667 | ACPI_FUNCTION_TRACE("acpi_bus_init"); | |
668 | ||
669 | status = acpi_os_initialize1(); | |
670 | ||
4be44fcd LB |
671 | status = |
672 | acpi_enable_subsystem(ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE); | |
1da177e4 | 673 | if (ACPI_FAILURE(status)) { |
4be44fcd LB |
674 | printk(KERN_ERR PREFIX |
675 | "Unable to start the ACPI Interpreter\n"); | |
1da177e4 LT |
676 | goto error1; |
677 | } | |
678 | ||
679 | if (ACPI_FAILURE(status)) { | |
4be44fcd LB |
680 | printk(KERN_ERR PREFIX |
681 | "Unable to initialize ACPI OS objects\n"); | |
1da177e4 LT |
682 | goto error1; |
683 | } | |
684 | #ifdef CONFIG_ACPI_EC | |
685 | /* | |
686 | * ACPI 2.0 requires the EC driver to be loaded and work before | |
687 | * the EC device is found in the namespace (i.e. before acpi_initialize_objects() | |
688 | * is called). | |
689 | * | |
690 | * This is accomplished by looking for the ECDT table, and getting | |
691 | * the EC parameters out of that. | |
692 | */ | |
693 | status = acpi_ec_ecdt_probe(); | |
694 | /* Ignore result. Not having an ECDT is not fatal. */ | |
695 | #endif | |
696 | ||
697 | status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); | |
698 | if (ACPI_FAILURE(status)) { | |
699 | printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n"); | |
700 | goto error1; | |
701 | } | |
702 | ||
703 | printk(KERN_INFO PREFIX "Interpreter enabled\n"); | |
704 | ||
705 | /* | |
706 | * Get the system interrupt model and evaluate \_PIC. | |
707 | */ | |
708 | result = acpi_bus_init_irq(); | |
709 | if (result) | |
710 | goto error1; | |
711 | ||
712 | /* | |
713 | * Register the for all standard device notifications. | |
714 | */ | |
4be44fcd LB |
715 | status = |
716 | acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, | |
717 | &acpi_bus_notify, NULL); | |
1da177e4 | 718 | if (ACPI_FAILURE(status)) { |
4be44fcd LB |
719 | printk(KERN_ERR PREFIX |
720 | "Unable to register for device notifications\n"); | |
1da177e4 LT |
721 | goto error1; |
722 | } | |
723 | ||
724 | /* | |
725 | * Create the top ACPI proc directory | |
726 | */ | |
727 | acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL); | |
728 | ||
729 | return_VALUE(0); | |
730 | ||
731 | /* Mimic structured exception handling */ | |
4be44fcd | 732 | error1: |
1da177e4 LT |
733 | acpi_terminate(); |
734 | return_VALUE(-ENODEV); | |
735 | } | |
736 | ||
4be44fcd | 737 | decl_subsys(acpi, NULL, NULL); |
1da177e4 | 738 | |
4be44fcd | 739 | static int __init acpi_init(void) |
1da177e4 | 740 | { |
4be44fcd | 741 | int result = 0; |
1da177e4 LT |
742 | |
743 | ACPI_FUNCTION_TRACE("acpi_init"); | |
744 | ||
4be44fcd | 745 | printk(KERN_INFO PREFIX "Subsystem revision %08x\n", ACPI_CA_VERSION); |
1da177e4 LT |
746 | |
747 | if (acpi_disabled) { | |
748 | printk(KERN_INFO PREFIX "Interpreter disabled.\n"); | |
749 | return_VALUE(-ENODEV); | |
750 | } | |
751 | ||
752 | firmware_register(&acpi_subsys); | |
753 | ||
754 | result = acpi_bus_init(); | |
755 | ||
756 | if (!result) { | |
757 | #ifdef CONFIG_PM | |
758 | if (!PM_IS_ACTIVE()) | |
759 | pm_active = 1; | |
760 | else { | |
4be44fcd LB |
761 | printk(KERN_INFO PREFIX |
762 | "APM is already active, exiting\n"); | |
1da177e4 LT |
763 | disable_acpi(); |
764 | result = -ENODEV; | |
765 | } | |
766 | #endif | |
767 | } else | |
768 | disable_acpi(); | |
769 | ||
770 | return_VALUE(result); | |
771 | } | |
772 | ||
773 | subsys_initcall(acpi_init); |