power.c 20.5 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 42 43 44 45 46
/*
 *  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>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

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

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

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

65 66 67 68 69 70
static struct acpi_device_id power_device_ids[] = {
	{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
struct acpi_power_reference {
	struct list_head node;
	struct acpi_device *device;
};

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

L
Len Brown 已提交
96
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
97

98
static const struct file_operations acpi_power_fops = {
99
	.owner = THIS_MODULE,
L
Len Brown 已提交
100 101 102 103
	.open = acpi_power_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
104 105 106 107 108 109 110
};

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

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


	if (!resource)
119
		return -ENODEV;
L
Linus Torvalds 已提交
120 121 122

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

127
	*resource = acpi_driver_data(device);
128
	if (!*resource)
129
		return -ENODEV;
L
Linus Torvalds 已提交
130

131
	return 0;
L
Linus Torvalds 已提交
132 133
}

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


142
	if (!handle || !state)
143
		return -EINVAL;
L
Linus Torvalds 已提交
144

145
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
L
Linus Torvalds 已提交
146
	if (ACPI_FAILURE(status))
147
		return -ENODEV;
L
Linus Torvalds 已提交
148

149 150
	*state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
			      ACPI_POWER_RESOURCE_STATE_OFF;
L
Linus Torvalds 已提交
151

152 153
	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);

L
Linus Torvalds 已提交
154
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
155
			  node_name,
156
				*state ? "on" : "off"));
L
Linus Torvalds 已提交
157

158
	return 0;
L
Linus Torvalds 已提交
159 160
}

L
Len Brown 已提交
161
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
162
{
163
	int result = 0, state1;
L
Len Brown 已提交
164
	u32 i = 0;
L
Linus Torvalds 已提交
165 166 167


	if (!list || !state)
168
		return -EINVAL;
L
Linus Torvalds 已提交
169 170

	/* The state of the list is 'on' IFF all resources are 'on'. */
171
	/* */
L
Linus Torvalds 已提交
172

L
Len Brown 已提交
173
	for (i = 0; i < list->count; i++) {
174 175 176 177 178 179
		/*
		 * 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 已提交
180
		if (result)
181
			return result;
L
Linus Torvalds 已提交
182

183
		*state = state1;
L
Linus Torvalds 已提交
184 185 186 187 188 189

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

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

192
	return result;
L
Linus Torvalds 已提交
193 194
}

195
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
196
{
197
	int result = 0, state;
198
	int found = 0;
L
Len Brown 已提交
199
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
200
	struct acpi_power_resource *resource = NULL;
201 202
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
203 204 205 206


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

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	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 已提交
234

235
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
L
Linus Torvalds 已提交
236
	if (ACPI_FAILURE(status))
237
		return -ENODEV;
L
Linus Torvalds 已提交
238

239 240 241 242 243 244 245 246 247 248 249 250
	if (!acpi_power_nocheck) {
		/*
		 * If acpi_power_nocheck is set, it is unnecessary to check
		 * the power state after power transition.
		 */
		result = acpi_power_get_state(resource->device->handle,
				&state);
		if (result)
			return result;
		if (state != ACPI_POWER_RESOURCE_STATE_ON)
			return -ENOEXEC;
	}
L
Linus Torvalds 已提交
251
	/* Update the power resource's _device_ power state */
252
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
253 254

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
L
Len Brown 已提交
255
			  resource->name));
256
	return 0;
L
Linus Torvalds 已提交
257 258
}

259
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
260
{
261
	int result = 0, state;
L
Len Brown 已提交
262
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
263
	struct acpi_power_resource *resource = NULL;
264 265 266
	struct list_head *node, *next;
	struct acpi_power_reference *ref;

L
Linus Torvalds 已提交
267 268 269

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

272 273 274 275 276 277 278 279 280 281 282
	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 已提交
283

284 285 286 287
	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);
288
		return 0;
L
Linus Torvalds 已提交
289
	}
290
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
291

292
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
L
Linus Torvalds 已提交
293
	if (ACPI_FAILURE(status))
294
		return -ENODEV;
L
Linus Torvalds 已提交
295

296 297 298 299 300 301 302 303 304 305 306
	if (!acpi_power_nocheck) {
		/*
		 * If acpi_power_nocheck is set, it is unnecessary to check
		 * the power state after power transition.
		 */
		result = acpi_power_get_state(handle, &state);
		if (result)
			return result;
		if (state != ACPI_POWER_RESOURCE_STATE_OFF)
			return -ENOEXEC;
	}
L
Linus Torvalds 已提交
307 308

	/* Update the power resource's _device_ power state */
309
	resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
310 311

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

314
	return 0;
L
Linus Torvalds 已提交
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 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
/**
 * 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 已提交
380 381 382
/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
383 384
 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
385
 */
386
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
L
Linus Torvalds 已提交
387
{
388
	int i, err;
L
Linus Torvalds 已提交
389 390

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

393 394 395 396 397 398 399
	/*
	 * Do not execute the code below twice in a row without calling
	 * acpi_disable_wakeup_device_power() in between for the same device
	 */
	if (dev->wakeup.flags.prepared)
		return 0;

L
Linus Torvalds 已提交
400 401
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
402
		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
403
		if (ret) {
404
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
405
			dev->wakeup.flags.valid = 0;
406
			return -ENODEV;
L
Linus Torvalds 已提交
407 408 409
		}
	}

410 411 412 413
	/*
	 * Passing 3 as the third argument below means the device may be placed
	 * in arbitrary power state afterwards.
	 */
414 415 416 417 418
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
	if (!err)
		dev->wakeup.flags.prepared = 1;

	return err;
L
Linus Torvalds 已提交
419 420 421 422
}

/*
 * Shutdown a wakeup device, counterpart of above method
423 424
 * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
425 426
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
427
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
428
{
429
	int i, ret;
L
Linus Torvalds 已提交
430 431

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

434 435 436 437 438 439 440 441 442
	/*
	 * Do not execute the code below twice in a row without calling
	 * acpi_enable_wakeup_device_power() in between for the same device
	 */
	if (!dev->wakeup.flags.prepared)
		return 0;

	dev->wakeup.flags.prepared = 0;

443 444 445
	ret = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
446 447 448

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
449
		ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
450
		if (ret) {
451
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
452
			dev->wakeup.flags.valid = 0;
453
			return -ENODEV;
L
Linus Torvalds 已提交
454 455 456
		}
	}

457
	return ret;
L
Linus Torvalds 已提交
458 459 460 461 462 463
}

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

L
Len Brown 已提交
464
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
465
{
L
Len Brown 已提交
466 467 468 469
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
470 471 472


	if (!device)
473
		return -EINVAL;
L
Linus Torvalds 已提交
474 475 476 477 478 479 480

	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 已提交
481
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
482 483 484 485 486 487
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
488
			return result;
L
Linus Torvalds 已提交
489 490 491

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
492
			return 0;
L
Linus Torvalds 已提交
493 494 495 496 497
		}
	}

	device->power.state = ACPI_STATE_D3;

498
	return 0;
L
Linus Torvalds 已提交
499 500
}

L
Len Brown 已提交
501
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
502
{
L
Len Brown 已提交
503 504 505 506
	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 已提交
507 508 509


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

L
Len Brown 已提交
512 513
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
514
		return -ENODEV;
L
Linus Torvalds 已提交
515 516 517 518 519 520 521 522 523 524

	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 已提交
525
	for (i = 0; i < tl->count; i++) {
526
		result = acpi_power_on(tl->handles[i], device);
L
Linus Torvalds 已提交
527 528 529 530
		if (result)
			goto end;
	}

531 532 533 534
	if (device->power.state == state) {
		goto end;
	}

L
Linus Torvalds 已提交
535 536 537
	/*
	 * Then we dereference all power resources used in the current list.
	 */
L
Len Brown 已提交
538
	for (i = 0; i < cl->count; i++) {
539
		result = acpi_power_off_device(cl->handles[i], device);
L
Linus Torvalds 已提交
540 541 542 543
		if (result)
			goto end;
	}

544
     end:
545
	if (result)
546
		device->power.state = ACPI_STATE_UNKNOWN;
547
	else {
548 549 550
	/* We shouldn't change the state till all above operations succeed */
		device->power.state = state;
	}
L
Linus Torvalds 已提交
551

552
	return result;
L
Linus Torvalds 已提交
553 554 555 556 557 558
}

/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
559
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
560 561 562

static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{
563
	int count = 0;
564
	int result = 0, state;
L
Linus Torvalds 已提交
565
	struct acpi_power_resource *resource = NULL;
566 567
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
568 569


570
	resource = seq->private;
L
Linus Torvalds 已提交
571 572 573 574

	if (!resource)
		goto end;

575
	result = acpi_power_get_state(resource->device->handle, &state);
576 577 578
	if (result)
		goto end;

L
Linus Torvalds 已提交
579
	seq_puts(seq, "state:                   ");
580
	switch (state) {
L
Linus Torvalds 已提交
581 582 583 584 585 586 587 588 589 590 591
	case ACPI_POWER_RESOURCE_STATE_ON:
		seq_puts(seq, "on\n");
		break;
	case ACPI_POWER_RESOURCE_STATE_OFF:
		seq_puts(seq, "off\n");
		break;
	default:
		seq_puts(seq, "unknown\n");
		break;
	}

592 593 594 595 596 597 598
	mutex_lock(&resource->resource_lock);
	list_for_each_safe(node, next, &resource->reference) {
		ref = container_of(node, struct acpi_power_reference, node);
		count++;
	}
	mutex_unlock(&resource->resource_lock);

L
Linus Torvalds 已提交
599
	seq_printf(seq, "system level:            S%d\n"
L
Len Brown 已提交
600 601 602
		   "order:                   %d\n"
		   "reference count:         %d\n",
		   resource->system_level,
603
		   resource->order, count);
L
Linus Torvalds 已提交
604

L
Len Brown 已提交
605
      end:
606
	return 0;
L
Linus Torvalds 已提交
607 608 609 610 611 612 613
}

static int acpi_power_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_power_seq_show, PDE(inode)->data);
}

L
Len Brown 已提交
614
static int acpi_power_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
615
{
L
Len Brown 已提交
616
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
617 618 619


	if (!device)
620
		return -EINVAL;
L
Linus Torvalds 已提交
621 622 623

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
624
						     acpi_power_dir);
L
Linus Torvalds 已提交
625
		if (!acpi_device_dir(device))
626
			return -ENODEV;
L
Linus Torvalds 已提交
627 628 629
	}

	/* 'status' [R] */
630 631 632
	entry = proc_create_data(ACPI_POWER_FILE_STATUS,
				 S_IRUGO, acpi_device_dir(device),
				 &acpi_power_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
633
	if (!entry)
634 635
		return -EIO;
	return 0;
L
Linus Torvalds 已提交
636 637
}

L
Len Brown 已提交
638
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
639 640 641 642 643 644 645 646 647
{

	if (acpi_device_dir(device)) {
		remove_proc_entry(ACPI_POWER_FILE_STATUS,
				  acpi_device_dir(device));
		remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
		acpi_device_dir(device) = NULL;
	}

648
	return 0;
L
Linus Torvalds 已提交
649 650 651 652 653 654
}

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

L
Len Brown 已提交
655
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
656
{
657
	int result = 0, state;
L
Len Brown 已提交
658
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
659
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
660 661
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
662 663 664


	if (!device)
665
		return -EINVAL;
L
Linus Torvalds 已提交
666

667
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
668
	if (!resource)
669
		return -ENOMEM;
L
Linus Torvalds 已提交
670

671
	resource->device = device;
672 673
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
674 675 676
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
677
	device->driver_data = resource;
L
Linus Torvalds 已提交
678 679

	/* Evalute the object to get the system level and resource order. */
680
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
681 682 683 684 685 686 687
	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;

688
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
689 690 691
	if (result)
		goto end;

692
	switch (state) {
L
Linus Torvalds 已提交
693 694 695 696 697 698 699 700 701 702 703 704 705 706
	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;
	}

	result = acpi_power_add_fs(device);
	if (result)
		goto end;
L
Len Brown 已提交
707

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

L
Len Brown 已提交
711
      end:
L
Linus Torvalds 已提交
712 713
	if (result)
		kfree(resource);
L
Len Brown 已提交
714

715
	return result;
L
Linus Torvalds 已提交
716 717
}

L
Len Brown 已提交
718
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
719 720
{
	struct acpi_power_resource *resource = NULL;
721
	struct list_head *node, *next;
L
Linus Torvalds 已提交
722 723 724


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

727
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
728 729 730

	acpi_power_remove_fs(device);

731 732 733 734 735 736 737 738
	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 已提交
739 740
	kfree(resource);

741
	return 0;
L
Linus Torvalds 已提交
742 743
}

744
static int acpi_power_resume(struct acpi_device *device)
745
{
746
	int result = 0, state;
747 748 749 750 751 752
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_reference *ref;

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

753
	resource = acpi_driver_data(device);
754

755
	result = acpi_power_get_state(device->handle, &state);
756 757 758 759
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
760
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
761 762 763 764 765 766 767 768 769 770 771
	    !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;
}

772
int __init acpi_power_init(void)
L
Linus Torvalds 已提交
773
{
L
Len Brown 已提交
774
	int result = 0;
L
Linus Torvalds 已提交
775 776 777 778 779

	INIT_LIST_HEAD(&acpi_power_resource_list);

	acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
	if (!acpi_power_dir)
780
		return -ENODEV;
L
Linus Torvalds 已提交
781 782 783 784

	result = acpi_bus_register_driver(&acpi_power_driver);
	if (result < 0) {
		remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
785
		return -ENODEV;
L
Linus Torvalds 已提交
786 787
	}

788
	return 0;
L
Linus Torvalds 已提交
789
}