bus.c 21.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
/*
 *  acpi_bus.c - ACPI Bus Driver ($Revision: 80 $)
 *
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
28
#include <linux/kernel.h>
Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32
33
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
34
#include <linux/acpi.h>
Linus Torvalds's avatar
Linus Torvalds committed
35
36
37
#ifdef CONFIG_X86
#include <asm/mpspec.h>
#endif
38
#include <linux/pci.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

42
43
#include "internal.h"

Linus Torvalds's avatar
Linus Torvalds committed
44
#define _COMPONENT		ACPI_BUS_COMPONENT
45
ACPI_MODULE_NAME("bus");
Linus Torvalds's avatar
Linus Torvalds committed
46

Len Brown's avatar
Len Brown committed
47
48
struct acpi_device *acpi_root;
struct proc_dir_entry *acpi_root_dir;
Linus Torvalds's avatar
Linus Torvalds committed
49
50
51
52
EXPORT_SYMBOL(acpi_root_dir);

#define STRUCT_TO_INT(s)	(*((int*)&s))

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
static int set_power_nocheck(const struct dmi_system_id *id)
{
	printk(KERN_NOTICE PREFIX "%s detected - "
		"disable power check in power transistion\n", id->ident);
	acpi_power_nocheck = 1;
	return 0;
}
static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table[] = {
	{
	set_power_nocheck, "HP Pavilion 05", {
	DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
	DMI_MATCH(DMI_SYS_VENDOR, "HP Pavilion 05"),
	DMI_MATCH(DMI_PRODUCT_VERSION, "2001211RE101GLEND") }, NULL},
	{},
};


Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
73
/* --------------------------------------------------------------------------
                                Device Management
   -------------------------------------------------------------------------- */

Len Brown's avatar
Len Brown committed
74
int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
Linus Torvalds's avatar
Linus Torvalds committed
75
{
Len Brown's avatar
Len Brown committed
76
	acpi_status status = AE_OK;
Linus Torvalds's avatar
Linus Torvalds committed
77
78
79


	if (!device)
80
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
81
82
83

	/* TBD: Support fixed-feature devices */

Len Brown's avatar
Len Brown committed
84
	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device);
Linus Torvalds's avatar
Linus Torvalds committed
85
	if (ACPI_FAILURE(status) || !*device) {
86
87
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
				  handle));
88
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
89
90
	}

91
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
92
}
Len Brown's avatar
Len Brown committed
93

Linus Torvalds's avatar
Linus Torvalds committed
94
95
EXPORT_SYMBOL(acpi_bus_get_device);

Len Brown's avatar
Len Brown committed
96
int acpi_bus_get_status(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
97
{
Len Brown's avatar
Len Brown committed
98
	acpi_status status = AE_OK;
99
	unsigned long long sta = 0;
Len Brown's avatar
Len Brown committed
100

Linus Torvalds's avatar
Linus Torvalds committed
101
102

	if (!device)
103
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
104
105
106
107
108

	/*
	 * Evaluate _STA if present.
	 */
	if (device->flags.dynamic_status) {
Len Brown's avatar
Len Brown committed
109
110
		status =
		    acpi_evaluate_integer(device->handle, "_STA", NULL, &sta);
Linus Torvalds's avatar
Linus Torvalds committed
111
		if (ACPI_FAILURE(status))
112
			return -ENODEV;
Len Brown's avatar
Len Brown committed
113
		STRUCT_TO_INT(device->status) = (int)sta;
Linus Torvalds's avatar
Linus Torvalds committed
114
115
116
	}

	/*
117
118
119
120
	 * According to ACPI spec some device can be present and functional
	 * even if the parent is not present but functional.
	 * In such conditions the child device should not inherit the status
	 * from the parent.
Linus Torvalds's avatar
Linus Torvalds committed
121
122
	 */
	else
123
124
125
		STRUCT_TO_INT(device->status) =
		    ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
		    ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;
Linus Torvalds's avatar
Linus Torvalds committed
126
127

	if (device->status.functional && !device->status.present) {
128
129
130
131
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: "
		       "functional but not present;\n",
			device->pnp.bus_id,
			(u32) STRUCT_TO_INT(device->status)));
Linus Torvalds's avatar
Linus Torvalds committed
132
133
	}

Len Brown's avatar
Len Brown committed
134
135
136
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n",
			  device->pnp.bus_id,
			  (u32) STRUCT_TO_INT(device->status)));
Linus Torvalds's avatar
Linus Torvalds committed
137

138
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
139
140
}

Len Brown's avatar
Len Brown committed
141
EXPORT_SYMBOL(acpi_bus_get_status);
Linus Torvalds's avatar
Linus Torvalds committed
142

Zhang Rui's avatar
Zhang Rui committed
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
void acpi_bus_private_data_handler(acpi_handle handle,
				   u32 function, void *context)
{
	return;
}
EXPORT_SYMBOL(acpi_bus_private_data_handler);

int acpi_bus_get_private_data(acpi_handle handle, void **data)
{
	acpi_status status = AE_OK;

	if (!*data)
		return -EINVAL;

	status = acpi_get_data(handle, acpi_bus_private_data_handler, data);
	if (ACPI_FAILURE(status) || !*data) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
				handle));
		return -ENODEV;
	}

	return 0;
}
EXPORT_SYMBOL(acpi_bus_get_private_data);

Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
171
/* --------------------------------------------------------------------------
                                 Power Management
   -------------------------------------------------------------------------- */

Len Brown's avatar
Len Brown committed
172
int acpi_bus_get_power(acpi_handle handle, int *state)
Linus Torvalds's avatar
Linus Torvalds committed
173
{
Len Brown's avatar
Len Brown committed
174
175
176
	int result = 0;
	acpi_status status = 0;
	struct acpi_device *device = NULL;
177
	unsigned long long psc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
178
179
180
181


	result = acpi_bus_get_device(handle, &device);
	if (result)
182
		return result;
Linus Torvalds's avatar
Linus Torvalds committed
183
184
185
186
187
188
189
190
191

	*state = ACPI_STATE_UNKNOWN;

	if (!device->flags.power_manageable) {
		/* TBD: Non-recursive algorithm for walking up hierarchy */
		if (device->parent)
			*state = device->parent->power.state;
		else
			*state = ACPI_STATE_D0;
Len Brown's avatar
Len Brown committed
192
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
193
		/*
194
		 * Get the device's power state either directly (via _PSC) or
Linus Torvalds's avatar
Linus Torvalds committed
195
196
197
		 * indirectly (via power resources).
		 */
		if (device->power.flags.explicit_get) {
Len Brown's avatar
Len Brown committed
198
199
			status = acpi_evaluate_integer(device->handle, "_PSC",
						       NULL, &psc);
Linus Torvalds's avatar
Linus Torvalds committed
200
			if (ACPI_FAILURE(status))
201
				return -ENODEV;
Len Brown's avatar
Len Brown committed
202
203
			device->power.state = (int)psc;
		} else if (device->power.flags.power_resources) {
Linus Torvalds's avatar
Linus Torvalds committed
204
205
			result = acpi_power_get_inferred_state(device);
			if (result)
206
				return result;
Linus Torvalds's avatar
Linus Torvalds committed
207
208
209
210
211
212
		}

		*state = device->power.state;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n",
Len Brown's avatar
Len Brown committed
213
			  device->pnp.bus_id, device->power.state));
Linus Torvalds's avatar
Linus Torvalds committed
214

215
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
216
217
}

Len Brown's avatar
Len Brown committed
218
EXPORT_SYMBOL(acpi_bus_get_power);
Linus Torvalds's avatar
Linus Torvalds committed
219

Len Brown's avatar
Len Brown committed
220
int acpi_bus_set_power(acpi_handle handle, int state)
Linus Torvalds's avatar
Linus Torvalds committed
221
{
Len Brown's avatar
Len Brown committed
222
223
224
225
	int result = 0;
	acpi_status status = AE_OK;
	struct acpi_device *device = NULL;
	char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' };
Linus Torvalds's avatar
Linus Torvalds committed
226
227
228
229


	result = acpi_bus_get_device(handle, &device);
	if (result)
230
		return result;
Linus Torvalds's avatar
Linus Torvalds committed
231
232

	if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
233
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
234
235
236
237

	/* Make sure this is a valid target state */

	if (!device->flags.power_manageable) {
238
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable\n",
239
				kobject_name(&device->dev.kobj)));
240
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
241
	}
242
	/*
243
	 * Get device's current power state
244
	 */
245
246
247
248
249
250
251
252
253
254
255
256
257
	if (!acpi_power_nocheck) {
		/*
		 * Maybe the incorrect power state is returned on the bogus
		 * bios, which is different with the real power state.
		 * For example: the bios returns D0 state and the real power
		 * state is D3. OS expects to set the device to D0 state. In
		 * such case if OS uses the power state returned by the BIOS,
		 * the device can't be transisted to the correct power state.
		 * So if the acpi_power_nocheck is set, it is unnecessary to
		 * get the power state by calling acpi_bus_get_power.
		 */
		acpi_bus_get_power(device->handle, &device->power.state);
	}
258
	if ((state == device->power.state) && !device->flags.force_power_state) {
259
260
261
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
				  state));
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
262
	}
263

Linus Torvalds's avatar
Linus Torvalds committed
264
	if (!device->power.states[state].flags.valid) {
265
		printk(KERN_WARNING PREFIX "Device does not support D%d\n", state);
266
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
267
268
	}
	if (device->parent && (state < device->parent->power.state)) {
269
		printk(KERN_WARNING PREFIX
270
			      "Cannot set device to a higher-powered"
271
			      " state than parent\n");
272
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
273
274
275
276
277
278
279
280
	}

	/*
	 * Transition Power
	 * ----------------
	 * On transitions to a high-powered state we first apply power (via
	 * power resources) then evalute _PSx.  Conversly for transitions to
	 * a lower-powered state.
281
	 */
Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
285
286
287
288
	if (state < device->power.state) {
		if (device->power.flags.power_resources) {
			result = acpi_power_transition(device, state);
			if (result)
				goto end;
		}
		if (device->power.states[state].flags.explicit_set) {
Len Brown's avatar
Len Brown committed
289
290
			status = acpi_evaluate_object(device->handle,
						      object_name, NULL, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
291
292
293
294
295
			if (ACPI_FAILURE(status)) {
				result = -ENODEV;
				goto end;
			}
		}
Len Brown's avatar
Len Brown committed
296
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
297
		if (device->power.states[state].flags.explicit_set) {
Len Brown's avatar
Len Brown committed
298
299
			status = acpi_evaluate_object(device->handle,
						      object_name, NULL, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
300
301
302
303
304
305
306
307
308
309
310
311
			if (ACPI_FAILURE(status)) {
				result = -ENODEV;
				goto end;
			}
		}
		if (device->power.flags.power_resources) {
			result = acpi_power_transition(device, state);
			if (result)
				goto end;
		}
	}

Len Brown's avatar
Len Brown committed
312
      end:
Linus Torvalds's avatar
Linus Torvalds committed
313
	if (result)
314
315
316
		printk(KERN_WARNING PREFIX
			      "Transitioning device [%s] to D%d\n",
			      device->pnp.bus_id, state);
317
318
	else {
		device->power.state = state;
Len Brown's avatar
Len Brown committed
319
320
321
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Device [%s] transitioned to D%d\n",
				  device->pnp.bus_id, state));
322
	}
Linus Torvalds's avatar
Linus Torvalds committed
323

324
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
325
326
}

Len Brown's avatar
Len Brown committed
327
EXPORT_SYMBOL(acpi_bus_set_power);
Linus Torvalds's avatar
Linus Torvalds committed
328

329
330
331
332
333
334
335
336
337
338
339
bool acpi_bus_power_manageable(acpi_handle handle)
{
	struct acpi_device *device;
	int result;

	result = acpi_bus_get_device(handle, &device);
	return result ? false : device->flags.power_manageable;
}

EXPORT_SYMBOL(acpi_bus_power_manageable);

340
341
342
343
344
345
346
347
348
349
350
bool acpi_bus_can_wakeup(acpi_handle handle)
{
	struct acpi_device *device;
	int result;

	result = acpi_bus_get_device(handle, &device);
	return result ? false : device->wakeup.flags.valid;
}

EXPORT_SYMBOL(acpi_bus_can_wakeup);

Linus Torvalds's avatar
Linus Torvalds committed
351
352
353
354
/* --------------------------------------------------------------------------
                                Event Management
   -------------------------------------------------------------------------- */

355
#ifdef CONFIG_ACPI_PROC_EVENT
Linus Torvalds's avatar
Linus Torvalds committed
356
357
358
359
360
static DEFINE_SPINLOCK(acpi_bus_event_lock);

LIST_HEAD(acpi_bus_event_list);
DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue);

Len Brown's avatar
Len Brown committed
361
extern int event_is_open;
Linus Torvalds's avatar
Linus Torvalds committed
362

363
int acpi_bus_generate_proc_event4(const char *device_class, const char *bus_id, u8 type, int data)
Linus Torvalds's avatar
Linus Torvalds committed
364
{
365
	struct acpi_bus_event *event;
Len Brown's avatar
Len Brown committed
366
	unsigned long flags = 0;
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369

	/* drop event on the floor if no one's listening */
	if (!event_is_open)
370
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
371
372
373

	event = kmalloc(sizeof(struct acpi_bus_event), GFP_ATOMIC);
	if (!event)
374
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
375

376
377
	strcpy(event->device_class, device_class);
	strcpy(event->bus_id, bus_id);
Linus Torvalds's avatar
Linus Torvalds committed
378
379
380
381
382
383
384
385
386
	event->type = type;
	event->data = data;

	spin_lock_irqsave(&acpi_bus_event_lock, flags);
	list_add_tail(&event->node, &acpi_bus_event_list);
	spin_unlock_irqrestore(&acpi_bus_event_lock, flags);

	wake_up_interruptible(&acpi_bus_event_queue);

387
	return 0;
388
389
390
391
392
393
394
395
396
397
398

}

EXPORT_SYMBOL_GPL(acpi_bus_generate_proc_event4);

int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data)
{
	if (!device)
		return -EINVAL;
	return acpi_bus_generate_proc_event4(device->pnp.device_class,
					     device->pnp.bus_id, type, data);
Linus Torvalds's avatar
Linus Torvalds committed
399
}
Len Brown's avatar
Len Brown committed
400

401
EXPORT_SYMBOL(acpi_bus_generate_proc_event);
Linus Torvalds's avatar
Linus Torvalds committed
402

Len Brown's avatar
Len Brown committed
403
int acpi_bus_receive_event(struct acpi_bus_event *event)
Linus Torvalds's avatar
Linus Torvalds committed
404
{
Len Brown's avatar
Len Brown committed
405
406
	unsigned long flags = 0;
	struct acpi_bus_event *entry = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
407
408
409
410
411

	DECLARE_WAITQUEUE(wait, current);


	if (!event)
412
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
413
414
415
416
417
418
419
420
421
422
423
424
425

	if (list_empty(&acpi_bus_event_list)) {

		set_current_state(TASK_INTERRUPTIBLE);
		add_wait_queue(&acpi_bus_event_queue, &wait);

		if (list_empty(&acpi_bus_event_list))
			schedule();

		remove_wait_queue(&acpi_bus_event_queue, &wait);
		set_current_state(TASK_RUNNING);

		if (signal_pending(current))
426
			return -ERESTARTSYS;
Linus Torvalds's avatar
Linus Torvalds committed
427
428
429
	}

	spin_lock_irqsave(&acpi_bus_event_lock, flags);
430
431
432
	if (!list_empty(&acpi_bus_event_list)) {
		entry = list_entry(acpi_bus_event_list.next,
				   struct acpi_bus_event, node);
Linus Torvalds's avatar
Linus Torvalds committed
433
		list_del(&entry->node);
434
	}
Linus Torvalds's avatar
Linus Torvalds committed
435
436
437
	spin_unlock_irqrestore(&acpi_bus_event_lock, flags);

	if (!entry)
438
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
439
440
441
442
443

	memcpy(event, entry, sizeof(struct acpi_bus_event));

	kfree(entry);

444
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
445
446
}

447
#endif	/* CONFIG_ACPI_PROC_EVENT */
Linus Torvalds's avatar
Linus Torvalds committed
448
449
450
451
452
453

/* --------------------------------------------------------------------------
                             Notification Handling
   -------------------------------------------------------------------------- */

static int
Len Brown's avatar
Len Brown committed
454
acpi_bus_check_device(struct acpi_device *device, int *status_changed)
Linus Torvalds's avatar
Linus Torvalds committed
455
{
Len Brown's avatar
Len Brown committed
456
	acpi_status status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
457
458
459
460
	struct acpi_device_status old_status;


	if (!device)
461
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

	if (status_changed)
		*status_changed = 0;

	old_status = device->status;

	/*
	 * Make sure this device's parent is present before we go about
	 * messing with the device.
	 */
	if (device->parent && !device->parent->status.present) {
		device->status = device->parent->status;
		if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) {
			if (status_changed)
				*status_changed = 1;
		}
478
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
479
480
481
482
	}

	status = acpi_bus_get_status(device);
	if (ACPI_FAILURE(status))
483
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
484
485

	if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
486
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
487
488
489

	if (status_changed)
		*status_changed = 1;
Len Brown's avatar
Len Brown committed
490

Linus Torvalds's avatar
Linus Torvalds committed
491
492
493
494
495
496
	/*
	 * Device Insertion/Removal
	 */
	if ((device->status.present) && !(old_status.present)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n"));
		/* TBD: Handle device insertion */
Len Brown's avatar
Len Brown committed
497
	} else if (!(device->status.present) && (old_status.present)) {
Linus Torvalds's avatar
Linus Torvalds committed
498
499
500
501
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
		/* TBD: Handle device removal */
	}

502
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
503
504
}

Len Brown's avatar
Len Brown committed
505
static int acpi_bus_check_scope(struct acpi_device *device)
Linus Torvalds's avatar
Linus Torvalds committed
506
{
Len Brown's avatar
Len Brown committed
507
508
	int result = 0;
	int status_changed = 0;
Linus Torvalds's avatar
Linus Torvalds committed
509
510
511


	if (!device)
512
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
513
514
515
516

	/* Status Change? */
	result = acpi_bus_check_device(device, &status_changed);
	if (result)
517
		return result;
Linus Torvalds's avatar
Linus Torvalds committed
518
519

	if (!status_changed)
520
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
521
522
523
524
525
526

	/*
	 * TBD: Enumerate child devices within this device's scope and
	 *       run acpi_bus_check_device()'s on them.
	 */

527
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
528
529
}

530
531
532
533
534
535
536
537
538
539
540
541
542
static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
int register_acpi_bus_notifier(struct notifier_block *nb)
{
	return blocking_notifier_chain_register(&acpi_bus_notify_list, nb);
}
EXPORT_SYMBOL_GPL(register_acpi_bus_notifier);

void unregister_acpi_bus_notifier(struct notifier_block *nb)
{
	blocking_notifier_chain_unregister(&acpi_bus_notify_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);

Linus Torvalds's avatar
Linus Torvalds committed
543
544
545
546
547
/**
 * acpi_bus_notify
 * ---------------
 * Callback for all 'system-level' device notifications (values 0x00-0x7F).
 */
Len Brown's avatar
Len Brown committed
548
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
549
{
Len Brown's avatar
Len Brown committed
550
551
	int result = 0;
	struct acpi_device *device = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
552

553
554
	blocking_notifier_call_chain(&acpi_bus_notify_list,
		type, (void *)handle);
Linus Torvalds's avatar
Linus Torvalds committed
555
556

	if (acpi_bus_get_device(handle, &device))
557
		return;
Linus Torvalds's avatar
Linus Torvalds committed
558
559
560
561

	switch (type) {

	case ACPI_NOTIFY_BUS_CHECK:
Len Brown's avatar
Len Brown committed
562
563
564
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received BUS CHECK notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
565
		result = acpi_bus_check_scope(device);
566
		/*
Linus Torvalds's avatar
Linus Torvalds committed
567
		 * TBD: We'll need to outsource certain events to non-ACPI
Len Brown's avatar
Len Brown committed
568
		 *      drivers via the device manager (device.c).
Linus Torvalds's avatar
Linus Torvalds committed
569
570
571
572
		 */
		break;

	case ACPI_NOTIFY_DEVICE_CHECK:
Len Brown's avatar
Len Brown committed
573
574
575
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received DEVICE CHECK notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
576
		result = acpi_bus_check_device(device, NULL);
577
		/*
Linus Torvalds's avatar
Linus Torvalds committed
578
		 * TBD: We'll need to outsource certain events to non-ACPI
Len Brown's avatar
Len Brown committed
579
		 *      drivers via the device manager (device.c).
Linus Torvalds's avatar
Linus Torvalds committed
580
581
582
583
		 */
		break;

	case ACPI_NOTIFY_DEVICE_WAKE:
Len Brown's avatar
Len Brown committed
584
585
586
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received DEVICE WAKE notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
587
588
589
590
		/* TBD */
		break;

	case ACPI_NOTIFY_EJECT_REQUEST:
Len Brown's avatar
Len Brown committed
591
592
593
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received EJECT REQUEST notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
594
595
596
597
		/* TBD */
		break;

	case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
Len Brown's avatar
Len Brown committed
598
599
600
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received DEVICE CHECK LIGHT notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
601
602
603
604
		/* TBD: Exactly what does 'light' mean? */
		break;

	case ACPI_NOTIFY_FREQUENCY_MISMATCH:
Len Brown's avatar
Len Brown committed
605
606
607
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received FREQUENCY MISMATCH notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
608
609
610
611
		/* TBD */
		break;

	case ACPI_NOTIFY_BUS_MODE_MISMATCH:
Len Brown's avatar
Len Brown committed
612
613
614
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received BUS MODE MISMATCH notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
615
616
617
618
		/* TBD */
		break;

	case ACPI_NOTIFY_POWER_FAULT:
Len Brown's avatar
Len Brown committed
619
620
621
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received POWER FAULT notification for device [%s]\n",
				  device->pnp.bus_id));
Linus Torvalds's avatar
Linus Torvalds committed
622
623
624
625
		/* TBD */
		break;

	default:
Len Brown's avatar
Len Brown committed
626
627
628
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Received unknown/unsupported notification [%08x]\n",
				  type));
Linus Torvalds's avatar
Linus Torvalds committed
629
630
631
		break;
	}

632
	return;
Linus Torvalds's avatar
Linus Torvalds committed
633
634
635
636
637
638
}

/* --------------------------------------------------------------------------
                             Initialization/Cleanup
   -------------------------------------------------------------------------- */

Len Brown's avatar
Len Brown committed
639
static int __init acpi_bus_init_irq(void)
Linus Torvalds's avatar
Linus Torvalds committed
640
{
Len Brown's avatar
Len Brown committed
641
642
643
644
	acpi_status status = AE_OK;
	union acpi_object arg = { ACPI_TYPE_INTEGER };
	struct acpi_object_list arg_list = { 1, &arg };
	char *message = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
645
646


647
	/*
Linus Torvalds's avatar
Linus Torvalds committed
648
649
650
651
652
653
654
655
656
657
658
659
660
661
	 * Let the system know what interrupt model we are using by
	 * evaluating the \_PIC object, if exists.
	 */

	switch (acpi_irq_model) {
	case ACPI_IRQ_MODEL_PIC:
		message = "PIC";
		break;
	case ACPI_IRQ_MODEL_IOAPIC:
		message = "IOAPIC";
		break;
	case ACPI_IRQ_MODEL_IOSAPIC:
		message = "IOSAPIC";
		break;
John Keller's avatar
John Keller committed
662
663
664
	case ACPI_IRQ_MODEL_PLATFORM:
		message = "platform specific model";
		break;
Linus Torvalds's avatar
Linus Torvalds committed
665
666
	default:
		printk(KERN_WARNING PREFIX "Unknown interrupt routing model\n");
667
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
668
669
670
671
672
673
674
675
	}

	printk(KERN_INFO PREFIX "Using %s for interrupt routing\n", message);

	arg.integer.value = acpi_irq_model;

	status = acpi_evaluate_object(NULL, "\\_PIC", &arg_list, NULL);
	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
676
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PIC"));
677
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
678
679
	}

680
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
681
682
}

683
u8 acpi_gbl_permanent_mmap;
684
685


Len Brown's avatar
Len Brown committed
686
void __init acpi_early_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
687
{
Len Brown's avatar
Len Brown committed
688
	acpi_status status = AE_OK;
Linus Torvalds's avatar
Linus Torvalds committed
689
690

	if (acpi_disabled)
691
		return;
Linus Torvalds's avatar
Linus Torvalds committed
692

Bob Moore's avatar
Bob Moore committed
693
694
	printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION);

Linus Torvalds's avatar
Linus Torvalds committed
695
696
697
698
	/* enable workarounds, unless strict ACPI spec. compliance */
	if (!acpi_strict)
		acpi_gbl_enable_interpreter_slack = TRUE;

699
700
701
702
703
704
705
706
707
	acpi_gbl_permanent_mmap = 1;

	status = acpi_reallocate_root_table();
	if (ACPI_FAILURE(status)) {
		printk(KERN_ERR PREFIX
		       "Unable to reallocate ACPI tables\n");
		goto error0;
	}

Linus Torvalds's avatar
Linus Torvalds committed
708
709
	status = acpi_initialize_subsystem();
	if (ACPI_FAILURE(status)) {
Len Brown's avatar
Len Brown committed
710
711
		printk(KERN_ERR PREFIX
		       "Unable to initialize the ACPI Interpreter\n");
Linus Torvalds's avatar
Linus Torvalds committed
712
713
714
715
716
		goto error0;
	}

	status = acpi_load_tables();
	if (ACPI_FAILURE(status)) {
Len Brown's avatar
Len Brown committed
717
718
		printk(KERN_ERR PREFIX
		       "Unable to load the System Description Tables\n");
Linus Torvalds's avatar
Linus Torvalds committed
719
720
721
722
723
724
		goto error0;
	}

#ifdef CONFIG_X86
	if (!acpi_ioapic) {
		/* compatible (0) means level (3) */
725
726
727
728
		if (!(acpi_sci_flags & ACPI_MADT_TRIGGER_MASK)) {
			acpi_sci_flags &= ~ACPI_MADT_TRIGGER_MASK;
			acpi_sci_flags |= ACPI_MADT_TRIGGER_LEVEL;
		}
Linus Torvalds's avatar
Linus Torvalds committed
729
		/* Set PIC-mode SCI trigger type */
730
		acpi_pic_sci_set_trigger(acpi_gbl_FADT.sci_interrupt,
731
					 (acpi_sci_flags & ACPI_MADT_TRIGGER_MASK) >> 2);
Linus Torvalds's avatar
Linus Torvalds committed
732
733
	} else {
		/*
734
		 * now that acpi_gbl_FADT is initialized,
Linus Torvalds's avatar
Linus Torvalds committed
735
736
		 * update it with result from INT_SRC_OVR parsing
		 */
737
		acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi;
Linus Torvalds's avatar
Linus Torvalds committed
738
739
740
	}
#endif

Len Brown's avatar
Len Brown committed
741
742
743
744
	status =
	    acpi_enable_subsystem(~
				  (ACPI_NO_HARDWARE_INIT |
				   ACPI_NO_ACPI_ENABLE));
Linus Torvalds's avatar
Linus Torvalds committed
745
746
747
748
749
	if (ACPI_FAILURE(status)) {
		printk(KERN_ERR PREFIX "Unable to enable ACPI\n");
		goto error0;
	}

750
	return;
Linus Torvalds's avatar
Linus Torvalds committed
751

Len Brown's avatar
Len Brown committed
752
      error0:
Linus Torvalds's avatar
Linus Torvalds committed
753
	disable_acpi();
754
	return;
Linus Torvalds's avatar
Linus Torvalds committed
755
756
}

Len Brown's avatar
Len Brown committed
757
static int __init acpi_bus_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
758
{
Len Brown's avatar
Len Brown committed
759
760
761
	int result = 0;
	acpi_status status = AE_OK;
	extern acpi_status acpi_os_initialize1(void);
Linus Torvalds's avatar
Linus Torvalds committed
762
763
764
765


	status = acpi_os_initialize1();

Len Brown's avatar
Len Brown committed
766
767
	status =
	    acpi_enable_subsystem(ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE);
Linus Torvalds's avatar
Linus Torvalds committed
768
	if (ACPI_FAILURE(status)) {
Len Brown's avatar
Len Brown committed
769
770
		printk(KERN_ERR PREFIX
		       "Unable to start the ACPI Interpreter\n");
Linus Torvalds's avatar
Linus Torvalds committed
771
772
773
774
		goto error1;
	}

	if (ACPI_FAILURE(status)) {
Len Brown's avatar
Len Brown committed
775
776
		printk(KERN_ERR PREFIX
		       "Unable to initialize ACPI OS objects\n");
Linus Torvalds's avatar
Linus Torvalds committed
777
778
		goto error1;
	}
Bjorn Helgaas's avatar
Bjorn Helgaas committed
779

Linus Torvalds's avatar
Linus Torvalds committed
780
781
782
783
784
	/*
	 * ACPI 2.0 requires the EC driver to be loaded and work before
	 * the EC device is found in the namespace (i.e. before acpi_initialize_objects()
	 * is called).
	 *
785
	 * This is accomplished by looking for the ECDT table, and getting
Linus Torvalds's avatar
Linus Torvalds committed
786
787
788
789
790
791
792
793
794
795
796
	 * the EC parameters out of that.
	 */
	status = acpi_ec_ecdt_probe();
	/* Ignore result. Not having an ECDT is not fatal. */

	status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
	if (ACPI_FAILURE(status)) {
		printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
		goto error1;
	}

797
798
799
800
801
802
	/*
	 * Maybe EC region is required at bus_scan/acpi_get_devices. So it
	 * is necessary to enable it as early as possible.
	 */
	acpi_boot_ec_enable();

Linus Torvalds's avatar
Linus Torvalds committed
803
804
	printk(KERN_INFO PREFIX "Interpreter enabled\n");

805
806
807
	/* Initialize sleep structures */
	acpi_sleep_init();

Linus Torvalds's avatar
Linus Torvalds committed
808
809
810
811
812
813
814
815
816
817
	/*
	 * Get the system interrupt model and evaluate \_PIC.
	 */
	result = acpi_bus_init_irq();
	if (result)
		goto error1;

	/*
	 * Register the for all standard device notifications.
	 */
Len Brown's avatar
Len Brown committed
818
819
820
	status =
	    acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY,
					&acpi_bus_notify, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
821
	if (ACPI_FAILURE(status)) {
Len Brown's avatar
Len Brown committed
822
823
		printk(KERN_ERR PREFIX
		       "Unable to register for device notifications\n");
Linus Torvalds's avatar
Linus Torvalds committed
824
825
826
827
828
829
830
831
		goto error1;
	}

	/*
	 * Create the top ACPI proc directory
	 */
	acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL);

832
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
833
834

	/* Mimic structured exception handling */
Len Brown's avatar
Len Brown committed
835
      error1:
Linus Torvalds's avatar
Linus Torvalds committed
836
	acpi_terminate();
837
	return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
838
839
}

840
struct kobject *acpi_kobj;
Linus Torvalds's avatar
Linus Torvalds committed
841

Len Brown's avatar
Len Brown committed
842
static int __init acpi_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
843
{
Len Brown's avatar
Len Brown committed
844
	int result = 0;
Linus Torvalds's avatar
Linus Torvalds committed
845
846
847
848


	if (acpi_disabled) {
		printk(KERN_INFO PREFIX "Interpreter disabled.\n");
849
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
850
851
	}

852
	acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
853
	if (!acpi_kobj) {
854
		printk(KERN_WARNING "%s: kset create error\n", __func__);
855
856
		acpi_kobj = NULL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
857
858
859
860

	result = acpi_bus_init();

	if (!result) {
861
		pci_mmcfg_late_init();
862
863
		if (!(pm_flags & PM_APM))
			pm_flags |= PM_ACPI;
Linus Torvalds's avatar
Linus Torvalds committed
864
		else {
Len Brown's avatar
Len Brown committed
865
866
			printk(KERN_INFO PREFIX
			       "APM is already active, exiting\n");
Linus Torvalds's avatar
Linus Torvalds committed
867
868
869
870
871
			disable_acpi();
			result = -ENODEV;
		}
	} else
		disable_acpi();
872
873
874
875

	if (acpi_disabled)
		return result;

876
877
878
879
880
	/*
	 * If the laptop falls into the DMI check table, the power state check
	 * will be disabled in the course of device power transistion.
	 */
	dmi_check_system(power_nocheck_dmi_table);
881
882

	acpi_scan_init();
883
	acpi_ec_init();
884
	acpi_power_init();
885
	acpi_system_init();
886
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
887
888
889
}

subsys_initcall(acpi_init);