power.c 17.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*
 *  acpi_power.c - ACPI Bus Power Management ($Revision: 39 $)
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  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.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

/*
 * ACPI power-managed devices may be controlled in two ways:
 * 1. via "Device Specific (D-State) Control"
 * 2. via "Power Resource Control".
 * This module is used to manage devices relying on Power Resource Control.
 * 
 * An ACPI "power resource object" describes a software controllable power
 * plane, clock plane, or other resource used by a power managed device.
 * A device may rely on multiple power resources, and a power resource
 * may be shared by multiple devices.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
42
#include <linux/slab.h>
L
Linus Torvalds 已提交
43 44
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
45 46
#include "sleep.h"

47 48
#define PREFIX "ACPI: "

49
#define _COMPONENT			ACPI_POWER_COMPONENT
50
ACPI_MODULE_NAME("power");
L
Linus Torvalds 已提交
51 52 53 54 55 56 57
#define ACPI_POWER_CLASS		"power_resource"
#define ACPI_POWER_DEVICE_NAME		"Power Resource"
#define ACPI_POWER_FILE_INFO		"info"
#define ACPI_POWER_FILE_STATUS		"state"
#define ACPI_POWER_RESOURCE_STATE_OFF	0x00
#define ACPI_POWER_RESOURCE_STATE_ON	0x01
#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
58 59 60 61

int acpi_power_nocheck;
module_param_named(power_nocheck, acpi_power_nocheck, bool, 000);

L
Len Brown 已提交
62 63
static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type);
64
static int acpi_power_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
65

66
static const struct acpi_device_id power_device_ids[] = {
67 68 69 70 71
	{ACPI_POWER_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, power_device_ids);

L
Linus Torvalds 已提交
72
static struct acpi_driver acpi_power_driver = {
L
Len Brown 已提交
73
	.name = "power",
L
Len Brown 已提交
74
	.class = ACPI_POWER_CLASS,
75
	.ids = power_device_ids,
L
Len Brown 已提交
76 77 78
	.ops = {
		.add = acpi_power_add,
		.remove = acpi_power_remove,
79
		.resume = acpi_power_resume,
L
Len Brown 已提交
80
		},
L
Linus Torvalds 已提交
81 82
};

83 84 85 86 87
struct acpi_power_reference {
	struct list_head node;
	struct acpi_device *device;
};

L
Len Brown 已提交
88
struct acpi_power_resource {
89
	struct acpi_device * device;
L
Len Brown 已提交
90 91 92
	acpi_bus_id name;
	u32 system_level;
	u32 order;
93 94
	struct mutex resource_lock;
	struct list_head reference;
L
Linus Torvalds 已提交
95 96
};

L
Len Brown 已提交
97
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
98 99 100 101 102 103

/* --------------------------------------------------------------------------
                             Power Resource Management
   -------------------------------------------------------------------------- */

static int
L
Len Brown 已提交
104 105
acpi_power_get_context(acpi_handle handle,
		       struct acpi_power_resource **resource)
L
Linus Torvalds 已提交
106
{
L
Len Brown 已提交
107 108
	int result = 0;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
109 110 111


	if (!resource)
112
		return -ENODEV;
L
Linus Torvalds 已提交
113 114 115

	result = acpi_bus_get_device(handle, &device);
	if (result) {
116
		printk(KERN_WARNING PREFIX "Getting context [%p]\n", handle);
117
		return result;
L
Linus Torvalds 已提交
118 119
	}

120
	*resource = acpi_driver_data(device);
121
	if (!*resource)
122
		return -ENODEV;
L
Linus Torvalds 已提交
123

124
	return 0;
L
Linus Torvalds 已提交
125 126
}

127
static int acpi_power_get_state(acpi_handle handle, int *state)
L
Linus Torvalds 已提交
128
{
L
Len Brown 已提交
129
	acpi_status status = AE_OK;
130
	unsigned long long sta = 0;
131 132
	char node_name[5];
	struct acpi_buffer buffer = { sizeof(node_name), node_name };
L
Linus Torvalds 已提交
133 134


135
	if (!handle || !state)
136
		return -EINVAL;
L
Linus Torvalds 已提交
137

138
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
L
Linus Torvalds 已提交
139
	if (ACPI_FAILURE(status))
140
		return -ENODEV;
L
Linus Torvalds 已提交
141

142 143
	*state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
			      ACPI_POWER_RESOURCE_STATE_OFF;
L
Linus Torvalds 已提交
144

145 146
	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);

L
Linus Torvalds 已提交
147
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
148
			  node_name,
149
				*state ? "on" : "off"));
L
Linus Torvalds 已提交
150

151
	return 0;
L
Linus Torvalds 已提交
152 153
}

L
Len Brown 已提交
154
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
155
{
156
	int result = 0, state1;
L
Len Brown 已提交
157
	u32 i = 0;
L
Linus Torvalds 已提交
158 159 160


	if (!list || !state)
161
		return -EINVAL;
L
Linus Torvalds 已提交
162 163 164

	/* The state of the list is 'on' IFF all resources are 'on'. */

L
Len Brown 已提交
165
	for (i = 0; i < list->count; i++) {
166 167 168 169 170 171
		/*
		 * The state of the power resource can be obtained by
		 * using the ACPI handle. In such case it is unnecessary to
		 * get the Power resource first and then get its state again.
		 */
		result = acpi_power_get_state(list->handles[i], &state1);
L
Linus Torvalds 已提交
172
		if (result)
173
			return result;
L
Linus Torvalds 已提交
174

175
		*state = state1;
L
Linus Torvalds 已提交
176 177 178 179 180 181

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
L
Len Brown 已提交
182
			  *state ? "on" : "off"));
L
Linus Torvalds 已提交
183

184
	return result;
L
Linus Torvalds 已提交
185 186
}

187
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
188
{
189
	int result = 0;
190
	int found = 0;
L
Len Brown 已提交
191
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
192
	struct acpi_power_resource *resource = NULL;
193 194
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
195 196 197 198


	result = acpi_power_get_context(handle, &resource);
	if (result)
199
		return result;
L
Linus Torvalds 已提交
200

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
	mutex_lock(&resource->resource_lock);
	list_for_each_safe(node, next, &resource->reference) {
		ref = container_of(node, struct acpi_power_reference, node);
		if (dev->handle == ref->device->handle) {
			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
				  dev->pnp.bus_id, resource->name));
			found = 1;
			break;
		}
	}

	if (!found) {
		ref = kmalloc(sizeof (struct acpi_power_reference),
		    irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
		if (!ref) {
			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
			mutex_unlock(&resource->resource_lock);
			return -ENOMEM;
		}
		list_add_tail(&ref->node, &resource->reference);
		ref->device = dev;
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
			  dev->pnp.bus_id, resource->name));
	}
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
226

227
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
L
Linus Torvalds 已提交
228
	if (ACPI_FAILURE(status))
229
		return -ENODEV;
L
Linus Torvalds 已提交
230 231

	/* Update the power resource's _device_ power state */
232
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
233 234

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
L
Len Brown 已提交
235
			  resource->name));
236
	return 0;
L
Linus Torvalds 已提交
237 238
}

239
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
240
{
241
	int result = 0;
L
Len Brown 已提交
242
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
243
	struct acpi_power_resource *resource = NULL;
244 245 246
	struct list_head *node, *next;
	struct acpi_power_reference *ref;

L
Linus Torvalds 已提交
247 248
	result = acpi_power_get_context(handle, &resource);
	if (result)
249
		return result;
L
Linus Torvalds 已提交
250

251 252 253 254 255 256 257 258 259 260 261
	mutex_lock(&resource->resource_lock);
	list_for_each_safe(node, next, &resource->reference) {
		ref = container_of(node, struct acpi_power_reference, node);
		if (dev->handle == ref->device->handle) {
			list_del(&ref->node);
			kfree(ref);
			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
			    dev->pnp.bus_id, resource->name));
			break;
		}
	}
L
Linus Torvalds 已提交
262

263 264 265 266
	if (!list_empty(&resource->reference)) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
		    resource->name));
		mutex_unlock(&resource->resource_lock);
267
		return 0;
L
Linus Torvalds 已提交
268
	}
269
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
270

271
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
L
Linus Torvalds 已提交
272
	if (ACPI_FAILURE(status))
273
		return -ENODEV;
L
Linus Torvalds 已提交
274 275

	/* Update the power resource's _device_ power state */
276
	resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
277 278

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
L
Len Brown 已提交
279
			  resource->name));
L
Linus Torvalds 已提交
280

281
	return 0;
L
Linus Torvalds 已提交
282 283
}

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
/**
 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
 *                          ACPI 3.0) _PSW (Power State Wake)
 * @dev: Device to handle.
 * @enable: 0 - disable, 1 - enable the wake capabilities of the device.
 * @sleep_state: Target sleep state of the system.
 * @dev_state: Target power state of the device.
 *
 * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 * State Wake) for the device, if present.  On failure reset the device's
 * wakeup.flags.valid flag.
 *
 * RETURN VALUE:
 * 0 if either _DSW or _PSW has been successfully executed
 * 0 if neither _DSW nor _PSW has been found
 * -ENODEV if the execution of either _DSW or _PSW has failed
 */
int acpi_device_sleep_wake(struct acpi_device *dev,
                           int enable, int sleep_state, int dev_state)
{
	union acpi_object in_arg[3];
	struct acpi_object_list arg_list = { 3, in_arg };
	acpi_status status = AE_OK;

	/*
	 * Try to execute _DSW first.
	 *
	 * Three agruments are needed for the _DSW object:
	 * Argument 0: enable/disable the wake capabilities
	 * Argument 1: target system state
	 * Argument 2: target device state
	 * When _DSW object is called to disable the wake capabilities, maybe
	 * the first argument is filled. The values of the other two agruments
	 * are meaningless.
	 */
	in_arg[0].type = ACPI_TYPE_INTEGER;
	in_arg[0].integer.value = enable;
	in_arg[1].type = ACPI_TYPE_INTEGER;
	in_arg[1].integer.value = sleep_state;
	in_arg[2].type = ACPI_TYPE_INTEGER;
	in_arg[2].integer.value = dev_state;
	status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL);
	if (ACPI_SUCCESS(status)) {
		return 0;
	} else if (status != AE_NOT_FOUND) {
		printk(KERN_ERR PREFIX "_DSW execution failed\n");
		dev->wakeup.flags.valid = 0;
		return -ENODEV;
	}

	/* Execute _PSW */
	arg_list.count = 1;
	in_arg[0].integer.value = enable;
	status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
		printk(KERN_ERR PREFIX "_PSW execution failed\n");
		dev->wakeup.flags.valid = 0;
		return -ENODEV;
	}

	return 0;
}

L
Linus Torvalds 已提交
347 348 349
/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
350 351
 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
352
 */
353
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
L
Linus Torvalds 已提交
354
{
355
	int i, err = 0;
L
Linus Torvalds 已提交
356 357

	if (!dev || !dev->wakeup.flags.valid)
358
		return -EINVAL;
L
Linus Torvalds 已提交
359

360 361 362 363
	mutex_lock(&acpi_device_lock);

	if (dev->wakeup.prepare_count++)
		goto out;
364

L
Linus Torvalds 已提交
365 366
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
367
		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
368
		if (ret) {
369
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
370
			dev->wakeup.flags.valid = 0;
371 372
			err = -ENODEV;
			goto err_out;
L
Linus Torvalds 已提交
373 374 375
		}
	}

376 377 378 379
	/*
	 * Passing 3 as the third argument below means the device may be placed
	 * in arbitrary power state afterwards.
	 */
380 381
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);

382 383 384 385 386 387
 err_out:
	if (err)
		dev->wakeup.prepare_count = 0;

 out:
	mutex_unlock(&acpi_device_lock);
388
	return err;
L
Linus Torvalds 已提交
389 390 391 392
}

/*
 * Shutdown a wakeup device, counterpart of above method
393 394
 * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
395 396
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
397
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
398
{
399
	int i, err = 0;
L
Linus Torvalds 已提交
400 401

	if (!dev || !dev->wakeup.flags.valid)
402
		return -EINVAL;
L
Linus Torvalds 已提交
403

404 405 406 407 408
	mutex_lock(&acpi_device_lock);

	if (--dev->wakeup.prepare_count > 0)
		goto out;

409
	/*
410 411
	 * Executing the code below even if prepare_count is already zero when
	 * the function is called may be useful, for example for initialisation.
412
	 */
413 414
	if (dev->wakeup.prepare_count < 0)
		dev->wakeup.prepare_count = 0;
415

416 417 418
	err = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (err)
		goto out;
L
Linus Torvalds 已提交
419 420 421

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
422 423
		int ret = acpi_power_off_device(
				dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
424
		if (ret) {
425
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
426
			dev->wakeup.flags.valid = 0;
427 428
			err = -ENODEV;
			goto out;
L
Linus Torvalds 已提交
429 430 431
		}
	}

432 433 434
 out:
	mutex_unlock(&acpi_device_lock);
	return err;
L
Linus Torvalds 已提交
435 436 437 438 439 440
}

/* --------------------------------------------------------------------------
                             Device Power Management
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
441
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
442
{
L
Len Brown 已提交
443 444 445 446
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
447 448 449


	if (!device)
450
		return -EINVAL;
L
Linus Torvalds 已提交
451 452 453 454 455 456 457

	device->power.state = ACPI_STATE_UNKNOWN;

	/*
	 * We know a device's inferred power state when all the resources
	 * required for a given D-state are 'on'.
	 */
L
Len Brown 已提交
458
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
459 460 461 462 463 464
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
465
			return result;
L
Linus Torvalds 已提交
466 467 468

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
469
			return 0;
L
Linus Torvalds 已提交
470 471 472 473 474
		}
	}

	device->power.state = ACPI_STATE_D3;

475
	return 0;
L
Linus Torvalds 已提交
476 477
}

L
Len Brown 已提交
478
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
479
{
L
Len Brown 已提交
480 481 482 483
	int result = 0;
	struct acpi_handle_list *cl = NULL;	/* Current Resources */
	struct acpi_handle_list *tl = NULL;	/* Target Resources */
	int i = 0;
L
Linus Torvalds 已提交
484 485 486


	if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
487
		return -EINVAL;
L
Linus Torvalds 已提交
488

L
Len Brown 已提交
489 490
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
491
		return -ENODEV;
L
Linus Torvalds 已提交
492 493 494 495 496 497 498 499 500 501

	cl = &device->power.states[device->power.state].resources;
	tl = &device->power.states[state].resources;

	/* TBD: Resources must be ordered. */

	/*
	 * First we reference all power resources required in the target list
	 * (e.g. so the device doesn't lose power while transitioning).
	 */
L
Len Brown 已提交
502
	for (i = 0; i < tl->count; i++) {
503
		result = acpi_power_on(tl->handles[i], device);
L
Linus Torvalds 已提交
504 505 506 507
		if (result)
			goto end;
	}

508 509 510 511
	if (device->power.state == state) {
		goto end;
	}

L
Linus Torvalds 已提交
512 513 514
	/*
	 * Then we dereference all power resources used in the current list.
	 */
L
Len Brown 已提交
515
	for (i = 0; i < cl->count; i++) {
516
		result = acpi_power_off_device(cl->handles[i], device);
L
Linus Torvalds 已提交
517 518 519 520
		if (result)
			goto end;
	}

521
     end:
522
	if (result)
523
		device->power.state = ACPI_STATE_UNKNOWN;
524
	else {
525 526 527
	/* We shouldn't change the state till all above operations succeed */
		device->power.state = state;
	}
L
Linus Torvalds 已提交
528

529
	return result;
L
Linus Torvalds 已提交
530 531 532 533 534 535
}

/* --------------------------------------------------------------------------
                                Driver Interface
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
536
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
537
{
538
	int result = 0, state;
L
Len Brown 已提交
539
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
540
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
541 542
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
543 544 545


	if (!device)
546
		return -EINVAL;
L
Linus Torvalds 已提交
547

548
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
549
	if (!resource)
550
		return -ENOMEM;
L
Linus Torvalds 已提交
551

552
	resource->device = device;
553 554
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
555 556 557
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
558
	device->driver_data = resource;
L
Linus Torvalds 已提交
559 560

	/* Evalute the object to get the system level and resource order. */
561
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
562 563 564 565 566 567 568
	if (ACPI_FAILURE(status)) {
		result = -ENODEV;
		goto end;
	}
	resource->system_level = acpi_object.power_resource.system_level;
	resource->order = acpi_object.power_resource.resource_order;

569
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
570 571 572
	if (result)
		goto end;

573
	switch (state) {
L
Linus Torvalds 已提交
574 575 576 577 578 579 580 581 582 583 584 585
	case ACPI_POWER_RESOURCE_STATE_ON:
		device->power.state = ACPI_STATE_D0;
		break;
	case ACPI_POWER_RESOURCE_STATE_OFF:
		device->power.state = ACPI_STATE_D3;
		break;
	default:
		device->power.state = ACPI_STATE_UNKNOWN;
		break;
	}

	printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
586
	       acpi_device_bid(device), state ? "on" : "off");
L
Linus Torvalds 已提交
587

L
Len Brown 已提交
588
      end:
L
Linus Torvalds 已提交
589 590
	if (result)
		kfree(resource);
L
Len Brown 已提交
591

592
	return result;
L
Linus Torvalds 已提交
593 594
}

L
Len Brown 已提交
595
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
596 597
{
	struct acpi_power_resource *resource = NULL;
598
	struct list_head *node, *next;
L
Linus Torvalds 已提交
599 600 601


	if (!device || !acpi_driver_data(device))
602
		return -EINVAL;
L
Linus Torvalds 已提交
603

604
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
605

606 607 608 609 610 611 612 613
	mutex_lock(&resource->resource_lock);
	list_for_each_safe(node, next, &resource->reference) {
		struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
		list_del(&ref->node);
		kfree(ref);
	}
	mutex_unlock(&resource->resource_lock);

L
Linus Torvalds 已提交
614 615
	kfree(resource);

616
	return 0;
L
Linus Torvalds 已提交
617 618
}

619
static int acpi_power_resume(struct acpi_device *device)
620
{
621
	int result = 0, state;
622 623 624 625 626 627
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_reference *ref;

	if (!device || !acpi_driver_data(device))
		return -EINVAL;

628
	resource = acpi_driver_data(device);
629

630
	result = acpi_power_get_state(device->handle, &state);
631 632 633 634
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
635
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
636 637 638 639 640 641 642 643 644 645 646
	    !list_empty(&resource->reference)) {
		ref = container_of(resource->reference.next, struct acpi_power_reference, node);
		mutex_unlock(&resource->resource_lock);
		result = acpi_power_on(device->handle, ref->device);
		return result;
	}

	mutex_unlock(&resource->resource_lock);
	return 0;
}

647
int __init acpi_power_init(void)
L
Linus Torvalds 已提交
648 649
{
	INIT_LIST_HEAD(&acpi_power_resource_list);
650
	return acpi_bus_register_driver(&acpi_power_driver);
L
Linus Torvalds 已提交
651
}