power.c 20.0 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>
43
#include <linux/pm_runtime.h>
L
Linus Torvalds 已提交
44 45
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
46
#include "sleep.h"
47
#include "internal.h"
48

49 50
#define PREFIX "ACPI: "

51
#define _COMPONENT			ACPI_POWER_COMPONENT
52
ACPI_MODULE_NAME("power");
L
Linus Torvalds 已提交
53 54 55 56 57 58 59
#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
60

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

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

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

82 83 84 85 86 87 88 89 90 91 92 93 94 95
/*
 * A power managed device
 * A device may rely on multiple power resources.
 * */
struct acpi_power_managed_device {
	struct device *dev; /* The physical device */
	acpi_handle *handle;
};

struct acpi_power_resource_device {
	struct acpi_power_managed_device *device;
	struct acpi_power_resource_device *next;
};

L
Len Brown 已提交
96
struct acpi_power_resource {
97
	struct acpi_device * device;
L
Len Brown 已提交
98 99 100
	acpi_bus_id name;
	u32 system_level;
	u32 order;
101
	unsigned int ref_count;
102
	struct mutex resource_lock;
103 104 105

	/* List of devices relying on this power resource */
	struct acpi_power_resource_device *devices;
L
Linus Torvalds 已提交
106 107
};

L
Len Brown 已提交
108
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
109 110 111 112 113 114

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

static int
L
Len Brown 已提交
115 116
acpi_power_get_context(acpi_handle handle,
		       struct acpi_power_resource **resource)
L
Linus Torvalds 已提交
117
{
L
Len Brown 已提交
118 119
	int result = 0;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
120 121 122


	if (!resource)
123
		return -ENODEV;
L
Linus Torvalds 已提交
124 125 126

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

131
	*resource = acpi_driver_data(device);
132
	if (!*resource)
133
		return -ENODEV;
L
Linus Torvalds 已提交
134

135
	return 0;
L
Linus Torvalds 已提交
136 137
}

138
static int acpi_power_get_state(acpi_handle handle, int *state)
L
Linus Torvalds 已提交
139
{
L
Len Brown 已提交
140
	acpi_status status = AE_OK;
141
	unsigned long long sta = 0;
142 143
	char node_name[5];
	struct acpi_buffer buffer = { sizeof(node_name), node_name };
L
Linus Torvalds 已提交
144 145


146
	if (!handle || !state)
147
		return -EINVAL;
L
Linus Torvalds 已提交
148

149
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
L
Linus Torvalds 已提交
150
	if (ACPI_FAILURE(status))
151
		return -ENODEV;
L
Linus Torvalds 已提交
152

153 154
	*state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
			      ACPI_POWER_RESOURCE_STATE_OFF;
L
Linus Torvalds 已提交
155

156 157
	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);

L
Linus Torvalds 已提交
158
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
159
			  node_name,
160
				*state ? "on" : "off"));
L
Linus Torvalds 已提交
161

162
	return 0;
L
Linus Torvalds 已提交
163 164
}

L
Len Brown 已提交
165
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
166
{
167 168
	int cur_state;
	int i = 0;
L
Linus Torvalds 已提交
169 170

	if (!list || !state)
171
		return -EINVAL;
L
Linus Torvalds 已提交
172 173 174

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

L
Len Brown 已提交
175
	for (i = 0; i < list->count; i++) {
176 177 178 179 180
		struct acpi_power_resource *resource;
		acpi_handle handle = list->handles[i];
		int result;

		result = acpi_power_get_context(handle, &resource);
L
Linus Torvalds 已提交
181
		if (result)
182
			return result;
L
Linus Torvalds 已提交
183

184 185 186
		mutex_lock(&resource->resource_lock);

		result = acpi_power_get_state(handle, &cur_state);
L
Linus Torvalds 已提交
187

188 189 190 191 192 193
		mutex_unlock(&resource->resource_lock);

		if (result)
			return result;

		if (cur_state != ACPI_POWER_RESOURCE_STATE_ON)
L
Linus Torvalds 已提交
194 195 196 197
			break;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
198
			  cur_state ? "on" : "off"));
L
Linus Torvalds 已提交
199

200 201 202
	*state = cur_state;

	return 0;
L
Linus Torvalds 已提交
203 204
}

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
/* Resume the device when all power resources in _PR0 are on */
static void acpi_power_on_device(struct acpi_power_managed_device *device)
{
	struct acpi_device *acpi_dev;
	acpi_handle handle = device->handle;
	int state;

	if (acpi_bus_get_device(handle, &acpi_dev))
		return;

	if(acpi_power_get_inferred_state(acpi_dev, &state))
		return;

	if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev))
		pm_request_resume(device->dev);
}

222
static int __acpi_power_on(struct acpi_power_resource *resource)
L
Linus Torvalds 已提交
223
{
224
	struct acpi_power_resource_device *device_list = resource->devices;
L
Len Brown 已提交
225
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
226

227 228 229 230 231 232 233 234 235 236
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	/* Update the power resource's _device_ power state */
	resource->device->power.state = ACPI_STATE_D0;

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
			  resource->name));

237 238 239 240 241 242
	while (device_list) {
		acpi_power_on_device(device_list->device);

		device_list = device_list->next;
	}

243 244 245 246 247 248 249
	return 0;
}

static int acpi_power_on(acpi_handle handle)
{
	int result = 0;
	struct acpi_power_resource *resource = NULL;
L
Linus Torvalds 已提交
250 251 252

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

255 256
	mutex_lock(&resource->resource_lock);

257 258 259 260 261 262
	if (resource->ref_count++) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Power resource [%s] already on",
				  resource->name));
	} else {
		result = __acpi_power_on(resource);
263 264
		if (result)
			resource->ref_count--;
265
	}
L
Linus Torvalds 已提交
266

267
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
268

269
	return result;
L
Linus Torvalds 已提交
270 271
}

272
static int acpi_power_off(acpi_handle handle)
L
Linus Torvalds 已提交
273
{
274
	int result = 0;
L
Len Brown 已提交
275
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
276
	struct acpi_power_resource *resource = NULL;
277

L
Linus Torvalds 已提交
278 279
	result = acpi_power_get_context(handle, &resource);
	if (result)
280
		return result;
L
Linus Torvalds 已提交
281

282
	mutex_lock(&resource->resource_lock);
283 284 285 286 287 288

	if (!resource->ref_count) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Power resource [%s] already off",
				  resource->name));
		goto unlock;
289
	}
L
Linus Torvalds 已提交
290

291 292 293 294 295
	if (--resource->ref_count) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Power resource [%s] still in use\n",
				  resource->name));
		goto unlock;
L
Linus Torvalds 已提交
296 297
	}

298
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
299 300 301 302 303
	if (ACPI_FAILURE(status)) {
		result = -ENODEV;
	} else {
		/* Update the power resource's _device_ power state */
		resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
304

305 306 307 308
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Power resource [%s] turned off\n",
				  resource->name));
	}
L
Linus Torvalds 已提交
309

310 311
 unlock:
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
312

313
	return result;
L
Linus Torvalds 已提交
314 315
}

316 317 318 319 320
static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res)
{
	int i;

	for (i = num_res - 1; i >= 0 ; i--)
321
		acpi_power_off(list->handles[i]);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
}

static void acpi_power_off_list(struct acpi_handle_list *list)
{
	__acpi_power_off_list(list, list->count);
}

static int acpi_power_on_list(struct acpi_handle_list *list)
{
	int result = 0;
	int i;

	for (i = 0; i < list->count; i++) {
		result = acpi_power_on(list->handles[i]);
		if (result) {
			__acpi_power_off_list(list, i);
			break;
		}
	}

	return result;
}

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
static void __acpi_power_resource_unregister_device(struct device *dev,
		acpi_handle res_handle)
{
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_resource_device *prev, *curr;

	if (acpi_power_get_context(res_handle, &resource))
		return;

	mutex_lock(&resource->resource_lock);
	prev = NULL;
	curr = resource->devices;
	while (curr) {
		if (curr->device->dev == dev) {
			if (!prev)
				resource->devices = curr->next;
			else
				prev->next = curr->next;

			kfree(curr);
			break;
		}

		prev = curr;
		curr = curr->next;
	}
	mutex_unlock(&resource->resource_lock);
}

/* Unlink dev from all power resources in _PR0 */
void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
{
	struct acpi_device *acpi_dev;
	struct acpi_handle_list *list;
	int i;

	if (!dev || !handle)
		return;

	if (acpi_bus_get_device(handle, &acpi_dev))
		return;

	list = &acpi_dev->power.states[ACPI_STATE_D0].resources;

	for (i = 0; i < list->count; i++)
		__acpi_power_resource_unregister_device(dev,
			list->handles[i]);
}
393
EXPORT_SYMBOL_GPL(acpi_power_resource_unregister_device);
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

static int __acpi_power_resource_register_device(
	struct acpi_power_managed_device *powered_device, acpi_handle handle)
{
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_resource_device *power_resource_device;
	int result;

	result = acpi_power_get_context(handle, &resource);
	if (result)
		return result;

	power_resource_device = kzalloc(
		sizeof(*power_resource_device), GFP_KERNEL);
	if (!power_resource_device)
		return -ENOMEM;

	power_resource_device->device = powered_device;

	mutex_lock(&resource->resource_lock);
	power_resource_device->next = resource->devices;
	resource->devices = power_resource_device;
	mutex_unlock(&resource->resource_lock);

	return 0;
}

/* Link dev to all power resources in _PR0 */
int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
{
	struct acpi_device *acpi_dev;
	struct acpi_handle_list *list;
	struct acpi_power_managed_device *powered_device;
	int i, ret;

	if (!dev || !handle)
		return -ENODEV;

	ret = acpi_bus_get_device(handle, &acpi_dev);
	if (ret)
		goto no_power_resource;

	if (!acpi_dev->power.flags.power_resources)
		goto no_power_resource;

	powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL);
	if (!powered_device)
		return -ENOMEM;

	powered_device->dev = dev;
	powered_device->handle = handle;

	list = &acpi_dev->power.states[ACPI_STATE_D0].resources;

	for (i = 0; i < list->count; i++) {
		ret = __acpi_power_resource_register_device(powered_device,
			list->handles[i]);

		if (ret) {
			acpi_power_resource_unregister_device(dev, handle);
			break;
		}
	}

	return ret;

no_power_resource:
	printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
	return -ENODEV;
}
464
EXPORT_SYMBOL_GPL(acpi_power_resource_register_device);
465

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
/**
 * 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 已提交
529 530 531
/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
532 533
 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
534
 */
535
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
L
Linus Torvalds 已提交
536
{
537
	int i, err = 0;
L
Linus Torvalds 已提交
538 539

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

542 543 544 545
	mutex_lock(&acpi_device_lock);

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

L
Linus Torvalds 已提交
547 548
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
549
		int ret = acpi_power_on(dev->wakeup.resources.handles[i]);
L
Linus Torvalds 已提交
550
		if (ret) {
551
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
552
			dev->wakeup.flags.valid = 0;
553 554
			err = -ENODEV;
			goto err_out;
L
Linus Torvalds 已提交
555 556 557
		}
	}

558 559 560 561
	/*
	 * Passing 3 as the third argument below means the device may be placed
	 * in arbitrary power state afterwards.
	 */
562 563
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);

564 565 566 567 568 569
 err_out:
	if (err)
		dev->wakeup.prepare_count = 0;

 out:
	mutex_unlock(&acpi_device_lock);
570
	return err;
L
Linus Torvalds 已提交
571 572 573 574
}

/*
 * Shutdown a wakeup device, counterpart of above method
575 576
 * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
577 578
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
579
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
580
{
581
	int i, err = 0;
L
Linus Torvalds 已提交
582 583

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

586 587 588 589 590
	mutex_lock(&acpi_device_lock);

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

591
	/*
592 593
	 * Executing the code below even if prepare_count is already zero when
	 * the function is called may be useful, for example for initialisation.
594
	 */
595 596
	if (dev->wakeup.prepare_count < 0)
		dev->wakeup.prepare_count = 0;
597

598 599 600
	err = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (err)
		goto out;
L
Linus Torvalds 已提交
601 602 603

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
604
		int ret = acpi_power_off(dev->wakeup.resources.handles[i]);
L
Linus Torvalds 已提交
605
		if (ret) {
606
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
607
			dev->wakeup.flags.valid = 0;
608 609
			err = -ENODEV;
			goto out;
L
Linus Torvalds 已提交
610 611 612
		}
	}

613 614 615
 out:
	mutex_unlock(&acpi_device_lock);
	return err;
L
Linus Torvalds 已提交
616 617 618 619 620 621
}

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

622
int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
L
Linus Torvalds 已提交
623
{
L
Len Brown 已提交
624 625 626 627
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
628

629
	if (!device || !state)
630
		return -EINVAL;
L
Linus Torvalds 已提交
631 632 633 634 635

	/*
	 * We know a device's inferred power state when all the resources
	 * required for a given D-state are 'on'.
	 */
636
	for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
L
Linus Torvalds 已提交
637 638 639 640 641 642
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
643
			return result;
L
Linus Torvalds 已提交
644 645

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
646
			*state = i;
647
			return 0;
L
Linus Torvalds 已提交
648 649 650
		}
	}

651
	*state = ACPI_STATE_D3;
652
	return 0;
L
Linus Torvalds 已提交
653 654
}

655 656 657 658 659 660 661 662
int acpi_power_on_resources(struct acpi_device *device, int state)
{
	if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3)
		return -EINVAL;

	return acpi_power_on_list(&device->power.states[state].resources);
}

L
Len Brown 已提交
663
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
664
{
665
	int result = 0;
L
Linus Torvalds 已提交
666

667
	if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
668
		return -EINVAL;
L
Linus Torvalds 已提交
669

670 671 672
	if (device->power.state == state)
		return 0;

L
Len Brown 已提交
673
	if ((device->power.state < ACPI_STATE_D0)
674
	    || (device->power.state > ACPI_STATE_D3_COLD))
675
		return -ENODEV;
L
Linus Torvalds 已提交
676 677 678 679 680

	/* TBD: Resources must be ordered. */

	/*
	 * First we reference all power resources required in the target list
681 682
	 * (e.g. so the device doesn't lose power while transitioning).  Then,
	 * we dereference all power resources used in the current list.
L
Linus Torvalds 已提交
683
	 */
684 685 686 687 688
	if (state < ACPI_STATE_D3_COLD)
		result = acpi_power_on_list(
			&device->power.states[state].resources);

	if (!result && device->power.state < ACPI_STATE_D3_COLD)
689 690
		acpi_power_off_list(
			&device->power.states[device->power.state].resources);
L
Linus Torvalds 已提交
691

692 693
	/* We shouldn't change the state unless the above operations succeed. */
	device->power.state = result ? ACPI_STATE_UNKNOWN : state;
L
Linus Torvalds 已提交
694

695
	return result;
L
Linus Torvalds 已提交
696 697 698 699 700 701
}

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

L
Len Brown 已提交
702
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
703
{
704
	int result = 0, state;
L
Len Brown 已提交
705
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
706
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
707 708
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
709 710 711


	if (!device)
712
		return -EINVAL;
L
Linus Torvalds 已提交
713

714
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
715
	if (!resource)
716
		return -ENOMEM;
L
Linus Torvalds 已提交
717

718
	resource->device = device;
719
	mutex_init(&resource->resource_lock);
L
Linus Torvalds 已提交
720 721 722
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
723
	device->driver_data = resource;
L
Linus Torvalds 已提交
724 725

	/* Evalute the object to get the system level and resource order. */
726
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
727 728 729 730 731 732 733
	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;

734
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
735 736 737
	if (result)
		goto end;

738
	switch (state) {
L
Linus Torvalds 已提交
739 740 741 742 743 744 745 746 747 748 749 750
	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),
751
	       acpi_device_bid(device), state ? "on" : "off");
L
Linus Torvalds 已提交
752

L
Len Brown 已提交
753
      end:
L
Linus Torvalds 已提交
754 755
	if (result)
		kfree(resource);
L
Len Brown 已提交
756

757
	return result;
L
Linus Torvalds 已提交
758 759
}

L
Len Brown 已提交
760
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
761
{
762
	struct acpi_power_resource *resource;
L
Linus Torvalds 已提交
763

764
	if (!device)
765
		return -EINVAL;
L
Linus Torvalds 已提交
766

767
	resource = acpi_driver_data(device);
768 769
	if (!resource)
		return -EINVAL;
770

L
Linus Torvalds 已提交
771 772
	kfree(resource);

773
	return 0;
L
Linus Torvalds 已提交
774 775
}

776
static int acpi_power_resume(struct acpi_device *device)
777
{
778
	int result = 0, state;
779
	struct acpi_power_resource *resource;
780

781
	if (!device)
782 783
		return -EINVAL;

784
	resource = acpi_driver_data(device);
785 786 787 788
	if (!resource)
		return -EINVAL;

	mutex_lock(&resource->resource_lock);
789

790
	result = acpi_power_get_state(device->handle, &state);
791
	if (result)
792
		goto unlock;
793

794 795
	if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count)
		result = __acpi_power_on(resource);
796

797
 unlock:
798
	mutex_unlock(&resource->resource_lock);
799 800

	return result;
801 802
}

803
int __init acpi_power_init(void)
L
Linus Torvalds 已提交
804 805
{
	INIT_LIST_HEAD(&acpi_power_resource_list);
806
	return acpi_bus_register_driver(&acpi_power_driver);
L
Linus Torvalds 已提交
807
}