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
/*
 *  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 60 61 62 63

#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 已提交
64 65
static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type);
66
static int acpi_power_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
67 68
static int acpi_power_open_fs(struct inode *inode, struct file *file);

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

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

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

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

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

102
static const struct file_operations acpi_power_fops = {
103
	.owner = THIS_MODULE,
L
Len Brown 已提交
104 105 106 107
	.open = acpi_power_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
108 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
	int result = 0, state1;
L
Len Brown 已提交
168
	u32 i = 0;
L
Linus Torvalds 已提交
169 170 171


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

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

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

187
		*state = state1;
L
Linus Torvalds 已提交
188 189 190 191 192 193

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

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

196
	return result;
L
Linus Torvalds 已提交
197 198
}

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


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

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

239
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
L
Linus Torvalds 已提交
240
	if (ACPI_FAILURE(status))
241
		return -ENODEV;
L
Linus Torvalds 已提交
242

243 244 245 246 247 248 249 250 251 252 253 254
	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 已提交
255
	/* Update the power resource's _device_ power state */
256
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
257 258

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
L
Len Brown 已提交
259
			  resource->name));
260
	return 0;
L
Linus Torvalds 已提交
261 262
}

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

L
Linus Torvalds 已提交
271 272 273

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

276 277 278 279 280 281 282 283 284 285 286
	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 已提交
287

288 289 290 291
	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);
292
		return 0;
L
Linus Torvalds 已提交
293
	}
294
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
295

296
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
L
Linus Torvalds 已提交
297
	if (ACPI_FAILURE(status))
298
		return -ENODEV;
L
Linus Torvalds 已提交
299

300 301 302 303 304 305 306 307 308 309 310
	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 已提交
311 312

	/* Update the power resource's _device_ power state */
313
	resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
314 315

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

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

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

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

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

	return err;
L
Linus Torvalds 已提交
423 424 425 426
}

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

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

438 439 440 441 442 443 444 445 446
	/*
	 * 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;

447 448 449
	ret = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
450 451 452

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

461
	return ret;
L
Linus Torvalds 已提交
462 463 464 465 466 467
}

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

L
Len Brown 已提交
468
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
469
{
L
Len Brown 已提交
470 471 472 473
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
474 475 476


	if (!device)
477
		return -EINVAL;
L
Linus Torvalds 已提交
478 479 480 481 482 483 484

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

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
492
			return result;
L
Linus Torvalds 已提交
493 494 495

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
496
			return 0;
L
Linus Torvalds 已提交
497 498 499 500 501
		}
	}

	device->power.state = ACPI_STATE_D3;

502
	return 0;
L
Linus Torvalds 已提交
503 504
}

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


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

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

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

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

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

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

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

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

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

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


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

	if (!resource)
		goto end;

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

	acpi_power_remove_fs(device);

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

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

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

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

757
	resource = acpi_driver_data(device);
758

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

	mutex_lock(&resource->resource_lock);
764
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
765 766 767 768 769 770 771 772 773 774 775
	    !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 已提交
776
static int __init acpi_power_init(void)
L
Linus Torvalds 已提交
777
{
L
Len Brown 已提交
778
	int result = 0;
L
Linus Torvalds 已提交
779 780 781


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

	INIT_LIST_HEAD(&acpi_power_resource_list);

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

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

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

subsys_initcall(acpi_power_init);