power.c 15.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 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
L
Len Brown 已提交
48
ACPI_MODULE_NAME("acpi_power")
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57
#define ACPI_POWER_COMPONENT		0x00800000
#define ACPI_POWER_CLASS		"power_resource"
#define ACPI_POWER_DRIVER_NAME		"ACPI Power Resource Driver"
#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
L
Len Brown 已提交
58 59
static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type);
L
Linus Torvalds 已提交
60 61 62
static int acpi_power_open_fs(struct inode *inode, struct file *file);

static struct acpi_driver acpi_power_driver = {
L
Len Brown 已提交
63 64 65 66 67 68 69
	.name = ACPI_POWER_DRIVER_NAME,
	.class = ACPI_POWER_CLASS,
	.ids = ACPI_POWER_HID,
	.ops = {
		.add = acpi_power_add,
		.remove = acpi_power_remove,
		},
L
Linus Torvalds 已提交
70 71
};

L
Len Brown 已提交
72 73
struct acpi_power_resource {
	acpi_handle handle;
74
	struct acpi_device * device;
L
Len Brown 已提交
75 76 77 78 79
	acpi_bus_id name;
	u32 system_level;
	u32 order;
	int state;
	int references;
L
Linus Torvalds 已提交
80 81
};

L
Len Brown 已提交
82
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
83 84

static struct file_operations acpi_power_fops = {
L
Len Brown 已提交
85 86 87 88
	.open = acpi_power_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
89 90 91 92 93 94 95
};

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

static int
L
Len Brown 已提交
96 97
acpi_power_get_context(acpi_handle handle,
		       struct acpi_power_resource **resource)
L
Linus Torvalds 已提交
98
{
L
Len Brown 已提交
99 100
	int result = 0;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
101 102 103


	if (!resource)
104
		return -ENODEV;
L
Linus Torvalds 已提交
105 106 107

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

L
Len Brown 已提交
112
	*resource = (struct acpi_power_resource *)acpi_driver_data(device);
L
Linus Torvalds 已提交
113
	if (!resource)
114
		return -ENODEV;
L
Linus Torvalds 已提交
115

116
	return 0;
L
Linus Torvalds 已提交
117 118
}

L
Len Brown 已提交
119
static int acpi_power_get_state(struct acpi_power_resource *resource)
L
Linus Torvalds 已提交
120
{
L
Len Brown 已提交
121 122
	acpi_status status = AE_OK;
	unsigned long sta = 0;
L
Linus Torvalds 已提交
123 124 125


	if (!resource)
126
		return -EINVAL;
L
Linus Torvalds 已提交
127 128 129

	status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta);
	if (ACPI_FAILURE(status))
130
		return -ENODEV;
L
Linus Torvalds 已提交
131 132 133 134 135 136 137

	if (sta & 0x01)
		resource->state = ACPI_POWER_RESOURCE_STATE_ON;
	else
		resource->state = ACPI_POWER_RESOURCE_STATE_OFF;

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
L
Len Brown 已提交
138
			  resource->name, resource->state ? "on" : "off"));
L
Linus Torvalds 已提交
139

140
	return 0;
L
Linus Torvalds 已提交
141 142
}

L
Len Brown 已提交
143
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
144
{
L
Len Brown 已提交
145
	int result = 0;
L
Linus Torvalds 已提交
146
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
147
	u32 i = 0;
L
Linus Torvalds 已提交
148 149 150


	if (!list || !state)
151
		return -EINVAL;
L
Linus Torvalds 已提交
152 153 154

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

L
Len Brown 已提交
155
	for (i = 0; i < list->count; i++) {
L
Linus Torvalds 已提交
156 157
		result = acpi_power_get_context(list->handles[i], &resource);
		if (result)
158
			return result;
L
Linus Torvalds 已提交
159 160
		result = acpi_power_get_state(resource);
		if (result)
161
			return result;
L
Linus Torvalds 已提交
162 163 164 165 166 167 168 169

		*state = resource->state;

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

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

172
	return result;
L
Linus Torvalds 已提交
173 174
}

L
Len Brown 已提交
175
static int acpi_power_on(acpi_handle handle)
L
Linus Torvalds 已提交
176
{
L
Len Brown 已提交
177 178 179
	int result = 0;
	acpi_status status = AE_OK;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
180 181 182 183 184
	struct acpi_power_resource *resource = NULL;


	result = acpi_power_get_context(handle, &resource);
	if (result)
185
		return result;
L
Linus Torvalds 已提交
186 187 188

	resource->references++;

L
Len Brown 已提交
189 190
	if ((resource->references > 1)
	    || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
L
Linus Torvalds 已提交
191
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
L
Len Brown 已提交
192
				  resource->name));
193
		return 0;
L
Linus Torvalds 已提交
194 195 196 197
	}

	status = acpi_evaluate_object(resource->handle, "_ON", NULL, NULL);
	if (ACPI_FAILURE(status))
198
		return -ENODEV;
L
Linus Torvalds 已提交
199 200 201

	result = acpi_power_get_state(resource);
	if (result)
202
		return result;
L
Linus Torvalds 已提交
203
	if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
204
		return -ENOEXEC;
L
Linus Torvalds 已提交
205 206

	/* Update the power resource's _device_ power state */
207 208
	device = resource->device;
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
209 210

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

213
	return 0;
L
Linus Torvalds 已提交
214 215
}

L
Len Brown 已提交
216
static int acpi_power_off_device(acpi_handle handle)
L
Linus Torvalds 已提交
217
{
L
Len Brown 已提交
218 219 220
	int result = 0;
	acpi_status status = AE_OK;
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
221 222 223 224 225
	struct acpi_power_resource *resource = NULL;


	result = acpi_power_get_context(handle, &resource);
	if (result)
226
		return result;
L
Linus Torvalds 已提交
227 228 229 230 231

	if (resource->references)
		resource->references--;

	if (resource->references) {
L
Len Brown 已提交
232 233 234
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Resource [%s] is still in use, dereferencing\n",
				  device->pnp.bus_id));
235
		return 0;
L
Linus Torvalds 已提交
236 237 238 239
	}

	if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
L
Len Brown 已提交
240
				  device->pnp.bus_id));
241
		return 0;
L
Linus Torvalds 已提交
242 243 244 245
	}

	status = acpi_evaluate_object(resource->handle, "_OFF", NULL, NULL);
	if (ACPI_FAILURE(status))
246
		return -ENODEV;
L
Linus Torvalds 已提交
247 248 249

	result = acpi_power_get_state(resource);
	if (result)
250
		return result;
L
Linus Torvalds 已提交
251
	if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
252
		return -ENOEXEC;
L
Linus Torvalds 已提交
253 254

	/* Update the power resource's _device_ power state */
255
	device = resource->device;
L
Linus Torvalds 已提交
256 257 258
	device->power.state = ACPI_STATE_D3;

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

261
	return 0;
L
Linus Torvalds 已提交
262 263 264 265 266 267 268
}

/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
 * 2. Enable _PSW (power state wake) for the device if present
 */
L
Len Brown 已提交
269
int acpi_enable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
270
{
L
Len Brown 已提交
271 272 273 274 275
	union acpi_object arg = { ACPI_TYPE_INTEGER };
	struct acpi_object_list arg_list = { 1, &arg };
	acpi_status status = AE_OK;
	int i;
	int ret = 0;
L
Linus Torvalds 已提交
276 277

	if (!dev || !dev->wakeup.flags.valid)
278
		return -1;
L
Linus Torvalds 已提交
279 280 281 282 283 284

	arg.integer.value = 1;
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
		ret = acpi_power_on(dev->wakeup.resources.handles[i]);
		if (ret) {
285
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
286
			dev->wakeup.flags.valid = 0;
287
			return -1;
L
Linus Torvalds 已提交
288 289 290 291 292 293
		}
	}

	/* Execute PSW */
	status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
294
		printk(KERN_ERR PREFIX "Evaluate _PSW\n");
L
Linus Torvalds 已提交
295 296 297 298
		dev->wakeup.flags.valid = 0;
		ret = -1;
	}

299
	return ret;
L
Linus Torvalds 已提交
300 301 302 303 304 305 306
}

/*
 * Shutdown a wakeup device, counterpart of above method
 * 1. Disable _PSW (power state wake)
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
307
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
308
{
L
Len Brown 已提交
309 310 311 312 313
	union acpi_object arg = { ACPI_TYPE_INTEGER };
	struct acpi_object_list arg_list = { 1, &arg };
	acpi_status status = AE_OK;
	int i;
	int ret = 0;
L
Linus Torvalds 已提交
314 315 316


	if (!dev || !dev->wakeup.flags.valid)
317
		return -1;
L
Linus Torvalds 已提交
318

L
Len Brown 已提交
319
	arg.integer.value = 0;
L
Linus Torvalds 已提交
320 321 322
	/* Execute PSW */
	status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
323
		printk(KERN_ERR PREFIX "Evaluate _PSW\n");
L
Linus Torvalds 已提交
324
		dev->wakeup.flags.valid = 0;
325
		return -1;
L
Linus Torvalds 已提交
326 327 328 329 330 331
	}

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
		ret = acpi_power_off_device(dev->wakeup.resources.handles[i]);
		if (ret) {
332
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
333
			dev->wakeup.flags.valid = 0;
334
			return -1;
L
Linus Torvalds 已提交
335 336 337
		}
	}

338
	return ret;
L
Linus Torvalds 已提交
339 340 341 342 343 344
}

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

L
Len Brown 已提交
345
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
346
{
L
Len Brown 已提交
347 348 349 350
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
351 352 353


	if (!device)
354
		return -EINVAL;
L
Linus Torvalds 已提交
355 356 357 358 359 360 361

	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 已提交
362
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
363 364 365 366 367 368
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
369
			return result;
L
Linus Torvalds 已提交
370 371 372

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
373
			return 0;
L
Linus Torvalds 已提交
374 375 376 377 378
		}
	}

	device->power.state = ACPI_STATE_D3;

379
	return 0;
L
Linus Torvalds 已提交
380 381
}

L
Len Brown 已提交
382
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
383
{
L
Len Brown 已提交
384 385 386 387
	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 已提交
388 389 390


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

L
Len Brown 已提交
393 394
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
395
		return -ENODEV;
L
Linus Torvalds 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

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

	device->power.state = ACPI_STATE_UNKNOWN;

	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 已提交
413
	for (i = 0; i < tl->count; i++) {
L
Linus Torvalds 已提交
414 415 416 417 418 419 420 421
		result = acpi_power_on(tl->handles[i]);
		if (result)
			goto end;
	}

	/*
	 * Then we dereference all power resources used in the current list.
	 */
L
Len Brown 已提交
422
	for (i = 0; i < cl->count; i++) {
L
Linus Torvalds 已提交
423 424 425 426 427 428 429
		result = acpi_power_off_device(cl->handles[i]);
		if (result)
			goto end;
	}

	/* We shouldn't change the state till all above operations succeed */
	device->power.state = state;
L
Len Brown 已提交
430
      end:
L
Linus Torvalds 已提交
431
	if (result)
432 433
		printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n",
			      device->pnp.bus_id, state);
L
Linus Torvalds 已提交
434

435
	return result;
L
Linus Torvalds 已提交
436 437 438 439 440 441
}

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

L
Len Brown 已提交
442
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467

static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{
	struct acpi_power_resource *resource = NULL;


	resource = (struct acpi_power_resource *)seq->private;

	if (!resource)
		goto end;

	seq_puts(seq, "state:                   ");
	switch (resource->state) {
	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;
	}

	seq_printf(seq, "system level:            S%d\n"
L
Len Brown 已提交
468 469 470 471
		   "order:                   %d\n"
		   "reference count:         %d\n",
		   resource->system_level,
		   resource->order, resource->references);
L
Linus Torvalds 已提交
472

L
Len Brown 已提交
473
      end:
474
	return 0;
L
Linus Torvalds 已提交
475 476 477 478 479 480 481
}

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 已提交
482
static int acpi_power_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
483
{
L
Len Brown 已提交
484
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
485 486 487


	if (!device)
488
		return -EINVAL;
L
Linus Torvalds 已提交
489 490 491

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
492
						     acpi_power_dir);
L
Linus Torvalds 已提交
493
		if (!acpi_device_dir(device))
494
			return -ENODEV;
L
Linus Torvalds 已提交
495 496 497 498
	}

	/* 'status' [R] */
	entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
L
Len Brown 已提交
499
				  S_IRUGO, acpi_device_dir(device));
L
Linus Torvalds 已提交
500
	if (!entry)
501
		return -EIO;
L
Linus Torvalds 已提交
502 503 504 505 506
	else {
		entry->proc_fops = &acpi_power_fops;
		entry->data = acpi_driver_data(device);
	}

507
	return 0;
L
Linus Torvalds 已提交
508 509
}

L
Len Brown 已提交
510
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
511 512 513 514 515 516 517 518 519
{

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

520
	return 0;
L
Linus Torvalds 已提交
521 522 523 524 525 526
}

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

L
Len Brown 已提交
527
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
528
{
L
Len Brown 已提交
529 530
	int result = 0;
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
531
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
532 533
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
534 535 536


	if (!device)
537
		return -EINVAL;
L
Linus Torvalds 已提交
538 539 540

	resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
	if (!resource)
541
		return -ENOMEM;
L
Linus Torvalds 已提交
542 543 544
	memset(resource, 0, sizeof(struct acpi_power_resource));

	resource->handle = device->handle;
545
	resource->device = device;
L
Linus Torvalds 已提交
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
	strcpy(resource->name, device->pnp.bus_id);
	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
	acpi_driver_data(device) = resource;

	/* Evalute the object to get the system level and resource order. */
	status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer);
	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;

	result = acpi_power_get_state(resource);
	if (result)
		goto end;

	switch (resource->state) {
	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 已提交
579

L
Linus Torvalds 已提交
580
	printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
L
Len Brown 已提交
581
	       acpi_device_bid(device), resource->state ? "on" : "off");
L
Linus Torvalds 已提交
582

L
Len Brown 已提交
583
      end:
L
Linus Torvalds 已提交
584 585
	if (result)
		kfree(resource);
L
Len Brown 已提交
586

587
	return result;
L
Linus Torvalds 已提交
588 589
}

L
Len Brown 已提交
590
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
591 592 593 594 595
{
	struct acpi_power_resource *resource = NULL;


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

L
Len Brown 已提交
598
	resource = (struct acpi_power_resource *)acpi_driver_data(device);
L
Linus Torvalds 已提交
599 600 601 602 603

	acpi_power_remove_fs(device);

	kfree(resource);

604
	return 0;
L
Linus Torvalds 已提交
605 606
}

L
Len Brown 已提交
607
static int __init acpi_power_init(void)
L
Linus Torvalds 已提交
608
{
L
Len Brown 已提交
609
	int result = 0;
L
Linus Torvalds 已提交
610 611 612


	if (acpi_disabled)
613
		return 0;
L
Linus Torvalds 已提交
614 615 616 617 618

	INIT_LIST_HEAD(&acpi_power_resource_list);

	acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
	if (!acpi_power_dir)
619
		return -ENODEV;
L
Linus Torvalds 已提交
620 621 622 623

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

627
	return 0;
L
Linus Torvalds 已提交
628 629 630
}

subsys_initcall(acpi_power_init);