power.c 20.7 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 47
/*
 *  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>

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

#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
#define MODULE_PARAM_PREFIX "acpi."
int acpi_power_nocheck;
module_param_named(power_nocheck, acpi_power_nocheck, bool, 000);

L
Len Brown 已提交
65 66
static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type);
67
static int acpi_power_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
68 69
static int acpi_power_open_fs(struct inode *inode, struct file *file);

70 71 72 73 74 75
static struct acpi_device_id power_device_ids[] = {
	{ACPI_POWER_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, power_device_ids);

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

87 88 89 90 91
struct acpi_power_reference {
	struct list_head node;
	struct acpi_device *device;
};

L
Len Brown 已提交
92
struct acpi_power_resource {
93
	struct acpi_device * device;
L
Len Brown 已提交
94 95 96
	acpi_bus_id name;
	u32 system_level;
	u32 order;
97 98
	struct mutex resource_lock;
	struct list_head reference;
L
Linus Torvalds 已提交
99 100
};

L
Len Brown 已提交
101
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
102

103
static const struct file_operations acpi_power_fops = {
104
	.owner = THIS_MODULE,
L
Len Brown 已提交
105 106 107 108
	.open = acpi_power_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
109 110 111 112 113 114 115
};

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

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


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

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

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

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

139
static int acpi_power_get_state(acpi_handle handle, int *state)
L
Linus Torvalds 已提交
140
{
L
Len Brown 已提交
141
	acpi_status status = AE_OK;
142
	unsigned long long sta = 0;
L
Linus Torvalds 已提交
143 144


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

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

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

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
156
			  acpi_ut_get_node_name(handle), 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 525 526 527 528 529

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

	if (!cl->count && !tl->count) {
		result = -ENODEV;
		goto end;
	}

	/* 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 已提交
530
	for (i = 0; i < tl->count; i++) {
531
		result = acpi_power_on(tl->handles[i], device);
L
Linus Torvalds 已提交
532 533 534 535
		if (result)
			goto end;
	}

536 537 538 539
	if (device->power.state == state) {
		goto end;
	}

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

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

557
	return result;
L
Linus Torvalds 已提交
558 559 560 561 562 563
}

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

L
Len Brown 已提交
564
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
565 566 567

static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{
568
	int count = 0;
569
	int result = 0, state;
L
Linus Torvalds 已提交
570
	struct acpi_power_resource *resource = NULL;
571 572
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
573 574


575
	resource = seq->private;
L
Linus Torvalds 已提交
576 577 578 579

	if (!resource)
		goto end;

580
	result = acpi_power_get_state(resource->device->handle, &state);
581 582 583
	if (result)
		goto end;

L
Linus Torvalds 已提交
584
	seq_puts(seq, "state:                   ");
585
	switch (state) {
L
Linus Torvalds 已提交
586 587 588 589 590 591 592 593 594 595 596
	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;
	}

597 598 599 600 601 602 603
	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 已提交
604
	seq_printf(seq, "system level:            S%d\n"
L
Len Brown 已提交
605 606 607
		   "order:                   %d\n"
		   "reference count:         %d\n",
		   resource->system_level,
608
		   resource->order, count);
L
Linus Torvalds 已提交
609

L
Len Brown 已提交
610
      end:
611
	return 0;
L
Linus Torvalds 已提交
612 613 614 615 616 617 618
}

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 已提交
619
static int acpi_power_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
620
{
L
Len Brown 已提交
621
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
622 623 624


	if (!device)
625
		return -EINVAL;
L
Linus Torvalds 已提交
626 627 628

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
629
						     acpi_power_dir);
L
Linus Torvalds 已提交
630
		if (!acpi_device_dir(device))
631
			return -ENODEV;
L
Linus Torvalds 已提交
632 633 634
	}

	/* 'status' [R] */
635 636 637
	entry = proc_create_data(ACPI_POWER_FILE_STATUS,
				 S_IRUGO, acpi_device_dir(device),
				 &acpi_power_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
638
	if (!entry)
639 640
		return -EIO;
	return 0;
L
Linus Torvalds 已提交
641 642
}

L
Len Brown 已提交
643
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
644 645 646 647 648 649 650 651 652
{

	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;
	}

653
	return 0;
L
Linus Torvalds 已提交
654 655 656 657 658 659
}

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

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


	if (!device)
670
		return -EINVAL;
L
Linus Torvalds 已提交
671

672
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
673
	if (!resource)
674
		return -ENOMEM;
L
Linus Torvalds 已提交
675

676
	resource->device = device;
677 678
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
679 680 681
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
682
	device->driver_data = resource;
L
Linus Torvalds 已提交
683 684

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

693
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
694 695 696
	if (result)
		goto end;

697
	switch (state) {
L
Linus Torvalds 已提交
698 699 700 701 702 703 704 705 706 707 708 709 710 711
	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 已提交
712

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

L
Len Brown 已提交
716
      end:
L
Linus Torvalds 已提交
717 718
	if (result)
		kfree(resource);
L
Len Brown 已提交
719

720
	return result;
L
Linus Torvalds 已提交
721 722
}

L
Len Brown 已提交
723
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
724 725
{
	struct acpi_power_resource *resource = NULL;
726
	struct list_head *node, *next;
L
Linus Torvalds 已提交
727 728 729


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

732
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
733 734 735

	acpi_power_remove_fs(device);

736 737 738 739 740 741 742 743
	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 已提交
744 745
	kfree(resource);

746
	return 0;
L
Linus Torvalds 已提交
747 748
}

749
static int acpi_power_resume(struct acpi_device *device)
750
{
751
	int result = 0, state;
752 753 754 755 756 757
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_reference *ref;

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

758
	resource = acpi_driver_data(device);
759

760
	result = acpi_power_get_state(device->handle, &state);
761 762 763 764
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
765
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
766 767 768 769 770 771 772 773 774 775 776
	    !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;
}

L
Len Brown 已提交
777
static int __init acpi_power_init(void)
L
Linus Torvalds 已提交
778
{
L
Len Brown 已提交
779
	int result = 0;
L
Linus Torvalds 已提交
780 781 782


	if (acpi_disabled)
783
		return 0;
L
Linus Torvalds 已提交
784 785 786 787 788

	INIT_LIST_HEAD(&acpi_power_resource_list);

	acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
	if (!acpi_power_dir)
789
		return -ENODEV;
L
Linus Torvalds 已提交
790 791 792 793

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

797
	return 0;
L
Linus Torvalds 已提交
798 799 800
}

subsys_initcall(acpi_power_init);