power.c 20.1 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
/*
 *  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>
46 47
#include "sleep.h"

48 49
#define PREFIX "ACPI: "

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

137
static int acpi_power_get_state(acpi_handle handle, int *state)
L
Linus Torvalds 已提交
138
{
L
Len Brown 已提交
139
	acpi_status status = AE_OK;
140
	unsigned long long sta = 0;
141 142
	char node_name[5];
	struct acpi_buffer buffer = { sizeof(node_name), node_name };
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 156
	acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);

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

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

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


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

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

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

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

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

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

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

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


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

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
	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 已提交
237

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

	/* Update the power resource's _device_ power state */
243
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
244 245

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
L
Len Brown 已提交
246
			  resource->name));
247
	return 0;
L
Linus Torvalds 已提交
248 249
}

250
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
251
{
252
	int result = 0;
L
Len Brown 已提交
253
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
254
	struct acpi_power_resource *resource = NULL;
255 256 257
	struct list_head *node, *next;
	struct acpi_power_reference *ref;

L
Linus Torvalds 已提交
258 259 260

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

263 264 265 266 267 268 269 270 271 272 273
	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 已提交
274

275 276 277 278
	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);
279
		return 0;
L
Linus Torvalds 已提交
280
	}
281
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
282

283
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
L
Linus Torvalds 已提交
284
	if (ACPI_FAILURE(status))
285
		return -ENODEV;
L
Linus Torvalds 已提交
286 287

	/* Update the power resource's _device_ power state */
288
	resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
289 290

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

293
	return 0;
L
Linus Torvalds 已提交
294 295
}

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
/**
 * 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 已提交
359 360 361
/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
362 363
 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
364
 */
365
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
L
Linus Torvalds 已提交
366
{
367
	int i, err = 0;
L
Linus Torvalds 已提交
368 369

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

372 373 374 375
	mutex_lock(&acpi_device_lock);

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

L
Linus Torvalds 已提交
377 378
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
379
		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
380
		if (ret) {
381
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
382
			dev->wakeup.flags.valid = 0;
383 384
			err = -ENODEV;
			goto err_out;
L
Linus Torvalds 已提交
385 386 387
		}
	}

388 389 390 391
	/*
	 * Passing 3 as the third argument below means the device may be placed
	 * in arbitrary power state afterwards.
	 */
392 393
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);

394 395 396 397 398 399
 err_out:
	if (err)
		dev->wakeup.prepare_count = 0;

 out:
	mutex_unlock(&acpi_device_lock);
400
	return err;
L
Linus Torvalds 已提交
401 402 403 404
}

/*
 * Shutdown a wakeup device, counterpart of above method
405 406
 * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
407 408
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
409
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
410
{
411
	int i, err = 0;
L
Linus Torvalds 已提交
412 413

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

416 417 418 419 420
	mutex_lock(&acpi_device_lock);

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

421
	/*
422 423
	 * Executing the code below even if prepare_count is already zero when
	 * the function is called may be useful, for example for initialisation.
424
	 */
425 426
	if (dev->wakeup.prepare_count < 0)
		dev->wakeup.prepare_count = 0;
427

428 429 430
	err = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (err)
		goto out;
L
Linus Torvalds 已提交
431 432 433

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
434 435
		int ret = acpi_power_off_device(
				dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
436
		if (ret) {
437
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
438
			dev->wakeup.flags.valid = 0;
439 440
			err = -ENODEV;
			goto out;
L
Linus Torvalds 已提交
441 442 443
		}
	}

444 445 446
 out:
	mutex_unlock(&acpi_device_lock);
	return err;
L
Linus Torvalds 已提交
447 448 449 450 451 452
}

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

L
Len Brown 已提交
453
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
454
{
L
Len Brown 已提交
455 456 457 458
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
459 460 461


	if (!device)
462
		return -EINVAL;
L
Linus Torvalds 已提交
463 464 465 466 467 468 469

	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 已提交
470
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
471 472 473 474 475 476
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
477
			return result;
L
Linus Torvalds 已提交
478 479 480

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
481
			return 0;
L
Linus Torvalds 已提交
482 483 484 485 486
		}
	}

	device->power.state = ACPI_STATE_D3;

487
	return 0;
L
Linus Torvalds 已提交
488 489
}

L
Len Brown 已提交
490
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
491
{
L
Len Brown 已提交
492 493 494 495
	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 已提交
496 497 498


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

L
Len Brown 已提交
501 502
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
503
		return -ENODEV;
L
Linus Torvalds 已提交
504 505 506 507 508 509 510 511 512 513

	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 已提交
514
	for (i = 0; i < tl->count; i++) {
515
		result = acpi_power_on(tl->handles[i], device);
L
Linus Torvalds 已提交
516 517 518 519
		if (result)
			goto end;
	}

520 521 522 523
	if (device->power.state == state) {
		goto end;
	}

L
Linus Torvalds 已提交
524 525 526
	/*
	 * Then we dereference all power resources used in the current list.
	 */
L
Len Brown 已提交
527
	for (i = 0; i < cl->count; i++) {
528
		result = acpi_power_off_device(cl->handles[i], device);
L
Linus Torvalds 已提交
529 530 531 532
		if (result)
			goto end;
	}

533
     end:
534
	if (result)
535
		device->power.state = ACPI_STATE_UNKNOWN;
536
	else {
537 538 539
	/* We shouldn't change the state till all above operations succeed */
		device->power.state = state;
	}
L
Linus Torvalds 已提交
540

541
	return result;
L
Linus Torvalds 已提交
542 543 544 545 546 547
}

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

L
Len Brown 已提交
548
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
549 550 551

static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{
552
	int count = 0;
553
	int result = 0, state;
L
Linus Torvalds 已提交
554
	struct acpi_power_resource *resource = NULL;
555 556
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
557 558


559
	resource = seq->private;
L
Linus Torvalds 已提交
560 561 562 563

	if (!resource)
		goto end;

564
	result = acpi_power_get_state(resource->device->handle, &state);
565 566 567
	if (result)
		goto end;

L
Linus Torvalds 已提交
568
	seq_puts(seq, "state:                   ");
569
	switch (state) {
L
Linus Torvalds 已提交
570 571 572 573 574 575 576 577 578 579 580
	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;
	}

581 582 583 584 585 586 587
	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 已提交
588
	seq_printf(seq, "system level:            S%d\n"
L
Len Brown 已提交
589 590 591
		   "order:                   %d\n"
		   "reference count:         %d\n",
		   resource->system_level,
592
		   resource->order, count);
L
Linus Torvalds 已提交
593

L
Len Brown 已提交
594
      end:
595
	return 0;
L
Linus Torvalds 已提交
596 597 598 599 600 601 602
}

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 已提交
603
static int acpi_power_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
604
{
L
Len Brown 已提交
605
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
606 607 608


	if (!device)
609
		return -EINVAL;
L
Linus Torvalds 已提交
610 611 612

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
613
						     acpi_power_dir);
L
Linus Torvalds 已提交
614
		if (!acpi_device_dir(device))
615
			return -ENODEV;
L
Linus Torvalds 已提交
616 617 618
	}

	/* 'status' [R] */
619 620 621
	entry = proc_create_data(ACPI_POWER_FILE_STATUS,
				 S_IRUGO, acpi_device_dir(device),
				 &acpi_power_fops, acpi_driver_data(device));
L
Linus Torvalds 已提交
622
	if (!entry)
623 624
		return -EIO;
	return 0;
L
Linus Torvalds 已提交
625 626
}

L
Len Brown 已提交
627
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
628 629 630 631 632 633 634 635 636
{

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

637
	return 0;
L
Linus Torvalds 已提交
638 639 640 641 642 643
}

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

L
Len Brown 已提交
644
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
645
{
646
	int result = 0, state;
L
Len Brown 已提交
647
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
648
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
649 650
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
651 652 653


	if (!device)
654
		return -EINVAL;
L
Linus Torvalds 已提交
655

656
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
657
	if (!resource)
658
		return -ENOMEM;
L
Linus Torvalds 已提交
659

660
	resource->device = device;
661 662
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
663 664 665
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
666
	device->driver_data = resource;
L
Linus Torvalds 已提交
667 668

	/* Evalute the object to get the system level and resource order. */
669
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
670 671 672 673 674 675 676
	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;

677
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
678 679 680
	if (result)
		goto end;

681
	switch (state) {
L
Linus Torvalds 已提交
682 683 684 685 686 687 688 689 690 691 692 693 694 695
	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 已提交
696

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

L
Len Brown 已提交
700
      end:
L
Linus Torvalds 已提交
701 702
	if (result)
		kfree(resource);
L
Len Brown 已提交
703

704
	return result;
L
Linus Torvalds 已提交
705 706
}

L
Len Brown 已提交
707
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
708 709
{
	struct acpi_power_resource *resource = NULL;
710
	struct list_head *node, *next;
L
Linus Torvalds 已提交
711 712 713


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

716
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
717 718 719

	acpi_power_remove_fs(device);

720 721 722 723 724 725 726 727
	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 已提交
728 729
	kfree(resource);

730
	return 0;
L
Linus Torvalds 已提交
731 732
}

733
static int acpi_power_resume(struct acpi_device *device)
734
{
735
	int result = 0, state;
736 737 738 739 740 741
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_reference *ref;

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

742
	resource = acpi_driver_data(device);
743

744
	result = acpi_power_get_state(device->handle, &state);
745 746 747 748
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
749
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
750 751 752 753 754 755 756 757 758 759 760
	    !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;
}

761
int __init acpi_power_init(void)
L
Linus Torvalds 已提交
762
{
L
Len Brown 已提交
763
	int result = 0;
L
Linus Torvalds 已提交
764 765 766 767 768

	INIT_LIST_HEAD(&acpi_power_resource_list);

	acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
	if (!acpi_power_dir)
769
		return -ENODEV;
L
Linus Torvalds 已提交
770 771 772 773

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

777
	return 0;
L
Linus Torvalds 已提交
778
}