power.c 18.4 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
L
Len Brown 已提交
57 58
static int acpi_power_add(struct acpi_device *device);
static int acpi_power_remove(struct acpi_device *device, int type);
59
static int acpi_power_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
60 61
static int acpi_power_open_fs(struct inode *inode, struct file *file);

62 63 64 65 66 67
static struct acpi_device_id power_device_ids[] = {
	{ACPI_POWER_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, power_device_ids);

L
Linus Torvalds 已提交
68
static struct acpi_driver acpi_power_driver = {
L
Len Brown 已提交
69
	.name = "power",
L
Len Brown 已提交
70
	.class = ACPI_POWER_CLASS,
71
	.ids = power_device_ids,
L
Len Brown 已提交
72 73 74
	.ops = {
		.add = acpi_power_add,
		.remove = acpi_power_remove,
75
		.resume = acpi_power_resume,
L
Len Brown 已提交
76
		},
L
Linus Torvalds 已提交
77 78
};

79 80 81 82 83
struct acpi_power_reference {
	struct list_head node;
	struct acpi_device *device;
};

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

L
Len Brown 已提交
94
static struct list_head acpi_power_resource_list;
L
Linus Torvalds 已提交
95

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

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

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


	if (!resource)
116
		return -ENODEV;
L
Linus Torvalds 已提交
117 118 119

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

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

128
	return 0;
L
Linus Torvalds 已提交
129 130
}

L
Len Brown 已提交
131
static int acpi_power_get_state(struct acpi_power_resource *resource)
L
Linus Torvalds 已提交
132
{
L
Len Brown 已提交
133 134
	acpi_status status = AE_OK;
	unsigned long sta = 0;
L
Linus Torvalds 已提交
135 136 137


	if (!resource)
138
		return -EINVAL;
L
Linus Torvalds 已提交
139

140
	status = acpi_evaluate_integer(resource->device->handle, "_STA", NULL, &sta);
L
Linus Torvalds 已提交
141
	if (ACPI_FAILURE(status))
142
		return -ENODEV;
L
Linus Torvalds 已提交
143 144 145 146 147 148 149

	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 已提交
150
			  resource->name, resource->state ? "on" : "off"));
L
Linus Torvalds 已提交
151

152
	return 0;
L
Linus Torvalds 已提交
153 154
}

L
Len Brown 已提交
155
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
156
{
L
Len Brown 已提交
157
	int result = 0;
L
Linus Torvalds 已提交
158
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
159
	u32 i = 0;
L
Linus Torvalds 已提交
160 161 162


	if (!list || !state)
163
		return -EINVAL;
L
Linus Torvalds 已提交
164 165 166

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

L
Len Brown 已提交
167
	for (i = 0; i < list->count; i++) {
L
Linus Torvalds 已提交
168 169
		result = acpi_power_get_context(list->handles[i], &resource);
		if (result)
170
			return result;
L
Linus Torvalds 已提交
171 172
		result = acpi_power_get_state(resource);
		if (result)
173
			return result;
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181

		*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 已提交
182
			  *state ? "on" : "off"));
L
Linus Torvalds 已提交
183

184
	return result;
L
Linus Torvalds 已提交
185 186
}

187
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
188
{
L
Len Brown 已提交
189
	int result = 0;
190
	int found = 0;
L
Len Brown 已提交
191
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
192
	struct acpi_power_resource *resource = NULL;
193 194
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
195 196 197 198


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

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
	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 已提交
226

227
	if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) {
L
Linus Torvalds 已提交
228
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
L
Len Brown 已提交
229
				  resource->name));
230
		return 0;
L
Linus Torvalds 已提交
231 232
	}

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

	result = acpi_power_get_state(resource);
	if (result)
239
		return result;
L
Linus Torvalds 已提交
240
	if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
241
		return -ENOEXEC;
L
Linus Torvalds 已提交
242 243

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

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

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

L
Linus Torvalds 已提交
259 260 261

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

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

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

	if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
286
				  resource->name));
287
		return 0;
L
Linus Torvalds 已提交
288 289
	}

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

	result = acpi_power_get_state(resource);
	if (result)
296
		return result;
L
Linus Torvalds 已提交
297
	if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
298
		return -ENOEXEC;
L
Linus Torvalds 已提交
299 300

	/* Update the power resource's _device_ power state */
301
	resource->device->power.state = ACPI_STATE_D3;
L
Linus Torvalds 已提交
302 303

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

306
	return 0;
L
Linus Torvalds 已提交
307 308 309 310 311 312 313
}

/*
 * 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 已提交
314
int acpi_enable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
315
{
L
Len Brown 已提交
316 317 318 319 320
	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 已提交
321 322

	if (!dev || !dev->wakeup.flags.valid)
323
		return -1;
L
Linus Torvalds 已提交
324 325 326 327

	arg.integer.value = 1;
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
328
		ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
329
		if (ret) {
330
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
331
			dev->wakeup.flags.valid = 0;
332
			return -1;
L
Linus Torvalds 已提交
333 334 335 336 337 338
		}
	}

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

344
	return ret;
L
Linus Torvalds 已提交
345 346 347 348 349 350 351
}

/*
 * Shutdown a wakeup device, counterpart of above method
 * 1. Disable _PSW (power state wake)
 * 2. Shutdown down the power resources
 */
L
Len Brown 已提交
352
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
L
Linus Torvalds 已提交
353
{
L
Len Brown 已提交
354 355 356 357 358
	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 已提交
359 360 361


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

L
Len Brown 已提交
364
	arg.integer.value = 0;
L
Linus Torvalds 已提交
365 366 367
	/* Execute PSW */
	status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
368
		printk(KERN_ERR PREFIX "Evaluate _PSW\n");
L
Linus Torvalds 已提交
369
		dev->wakeup.flags.valid = 0;
370
		return -1;
L
Linus Torvalds 已提交
371 372 373 374
	}

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
375
		ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
376
		if (ret) {
377
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
378
			dev->wakeup.flags.valid = 0;
379
			return -1;
L
Linus Torvalds 已提交
380 381 382
		}
	}

383
	return ret;
L
Linus Torvalds 已提交
384 385 386 387 388 389
}

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

L
Len Brown 已提交
390
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
391
{
L
Len Brown 已提交
392 393 394 395
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
396 397 398


	if (!device)
399
		return -EINVAL;
L
Linus Torvalds 已提交
400 401 402 403 404 405 406

	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 已提交
407
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
408 409 410 411 412 413
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
414
			return result;
L
Linus Torvalds 已提交
415 416 417

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
418
			return 0;
L
Linus Torvalds 已提交
419 420 421 422 423
		}
	}

	device->power.state = ACPI_STATE_D3;

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

L
Len Brown 已提交
427
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
428
{
L
Len Brown 已提交
429 430 431 432
	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 已提交
433 434 435


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

L
Len Brown 已提交
438 439
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
440
		return -ENODEV;
L
Linus Torvalds 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

	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 已提交
456
	for (i = 0; i < tl->count; i++) {
457
		result = acpi_power_on(tl->handles[i], device);
L
Linus Torvalds 已提交
458 459 460 461
		if (result)
			goto end;
	}

462 463 464 465
	if (device->power.state == state) {
		goto end;
	}

L
Linus Torvalds 已提交
466 467 468
	/*
	 * Then we dereference all power resources used in the current list.
	 */
L
Len Brown 已提交
469
	for (i = 0; i < cl->count; i++) {
470
		result = acpi_power_off_device(cl->handles[i], device);
L
Linus Torvalds 已提交
471 472 473 474
		if (result)
			goto end;
	}

475 476 477
     end:
	if (result) {
		device->power.state = ACPI_STATE_UNKNOWN;
478 479
		printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n",
			      device->pnp.bus_id, state);
480 481 482 483
	} else {
	/* We shouldn't change the state till all above operations succeed */
		device->power.state = state;
	}
L
Linus Torvalds 已提交
484

485
	return result;
L
Linus Torvalds 已提交
486 487 488 489 490 491
}

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

L
Len Brown 已提交
492
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
493 494 495

static int acpi_power_seq_show(struct seq_file *seq, void *offset)
{
496 497
	int count = 0;
	int result = 0;
L
Linus Torvalds 已提交
498
	struct acpi_power_resource *resource = NULL;
499 500
	struct list_head *node, *next;
	struct acpi_power_reference *ref;
L
Linus Torvalds 已提交
501 502


503
	resource = seq->private;
L
Linus Torvalds 已提交
504 505 506 507

	if (!resource)
		goto end;

508 509 510 511
	result = acpi_power_get_state(resource);
	if (result)
		goto end;

L
Linus Torvalds 已提交
512 513 514 515 516 517 518 519 520 521 522 523 524
	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;
	}

525 526 527 528 529 530 531
	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 已提交
532
	seq_printf(seq, "system level:            S%d\n"
L
Len Brown 已提交
533 534 535
		   "order:                   %d\n"
		   "reference count:         %d\n",
		   resource->system_level,
536
		   resource->order, count);
L
Linus Torvalds 已提交
537

L
Len Brown 已提交
538
      end:
539
	return 0;
L
Linus Torvalds 已提交
540 541 542 543 544 545 546
}

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 已提交
547
static int acpi_power_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
548
{
L
Len Brown 已提交
549
	struct proc_dir_entry *entry = NULL;
L
Linus Torvalds 已提交
550 551 552


	if (!device)
553
		return -EINVAL;
L
Linus Torvalds 已提交
554 555 556

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
557
						     acpi_power_dir);
L
Linus Torvalds 已提交
558
		if (!acpi_device_dir(device))
559
			return -ENODEV;
L
Linus Torvalds 已提交
560 561 562 563
	}

	/* 'status' [R] */
	entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
L
Len Brown 已提交
564
				  S_IRUGO, acpi_device_dir(device));
L
Linus Torvalds 已提交
565
	if (!entry)
566
		return -EIO;
L
Linus Torvalds 已提交
567 568 569 570 571
	else {
		entry->proc_fops = &acpi_power_fops;
		entry->data = acpi_driver_data(device);
	}

572
	return 0;
L
Linus Torvalds 已提交
573 574
}

L
Len Brown 已提交
575
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
576 577 578 579 580 581 582 583 584
{

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

585
	return 0;
L
Linus Torvalds 已提交
586 587 588 589 590 591
}

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

L
Len Brown 已提交
592
static int acpi_power_add(struct acpi_device *device)
L
Linus Torvalds 已提交
593
{
L
Len Brown 已提交
594 595
	int result = 0;
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
596
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
597 598
	union acpi_object acpi_object;
	struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
L
Linus Torvalds 已提交
599 600 601


	if (!device)
602
		return -EINVAL;
L
Linus Torvalds 已提交
603

604
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
605
	if (!resource)
606
		return -ENOMEM;
L
Linus Torvalds 已提交
607

608
	resource->device = device;
609 610
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
611 612 613 614 615 616
	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. */
617
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
	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 已提交
644

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

L
Len Brown 已提交
648
      end:
L
Linus Torvalds 已提交
649 650
	if (result)
		kfree(resource);
L
Len Brown 已提交
651

652
	return result;
L
Linus Torvalds 已提交
653 654
}

L
Len Brown 已提交
655
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
656 657
{
	struct acpi_power_resource *resource = NULL;
658
	struct list_head *node, *next;
L
Linus Torvalds 已提交
659 660 661


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

664
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
665 666 667

	acpi_power_remove_fs(device);

668 669 670 671 672 673 674 675
	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 已提交
676 677
	kfree(resource);

678
	return 0;
L
Linus Torvalds 已提交
679 680
}

681
static int acpi_power_resume(struct acpi_device *device)
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
{
	int result = 0;
	struct acpi_power_resource *resource = NULL;
	struct acpi_power_reference *ref;

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

	resource = (struct acpi_power_resource *)acpi_driver_data(device);

	result = acpi_power_get_state(resource);
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
	if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) &&
	    !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 已提交
709
static int __init acpi_power_init(void)
L
Linus Torvalds 已提交
710
{
L
Len Brown 已提交
711
	int result = 0;
L
Linus Torvalds 已提交
712 713 714


	if (acpi_disabled)
715
		return 0;
L
Linus Torvalds 已提交
716 717 718 719 720

	INIT_LIST_HEAD(&acpi_power_resource_list);

	acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
	if (!acpi_power_dir)
721
		return -ENODEV;
L
Linus Torvalds 已提交
722 723 724 725

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

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

subsys_initcall(acpi_power_init);