power.c 20.2 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
	acpi_bus_id name;
	u32 system_level;
	u32 order;
89 90
	struct mutex resource_lock;
	struct list_head reference;
L
Linus Torvalds 已提交
91 92
};

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

95
static const struct file_operations acpi_power_fops = {
96
	.owner = THIS_MODULE,
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);
125
	if (!*resource)
126
		return -ENODEV;
L
Linus Torvalds 已提交
127

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

131
static int acpi_power_get_state(acpi_handle handle, int *state)
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 (!handle || !state)
138
		return -EINVAL;
L
Linus Torvalds 已提交
139

140
	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
L
Linus Torvalds 已提交
141
	if (ACPI_FAILURE(status))
142
		return -ENODEV;
L
Linus Torvalds 已提交
143

144 145
	*state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
			      ACPI_POWER_RESOURCE_STATE_OFF;
L
Linus Torvalds 已提交
146 147

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
148
			  acpi_ut_get_node_name(handle), state ? "on" : "off"));
L
Linus Torvalds 已提交
149

150
	return 0;
L
Linus Torvalds 已提交
151 152
}

L
Len Brown 已提交
153
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
L
Linus Torvalds 已提交
154
{
155
	int result = 0, state1;
L
Len Brown 已提交
156
	u32 i = 0;
L
Linus Torvalds 已提交
157 158 159


	if (!list || !state)
160
		return -EINVAL;
L
Linus Torvalds 已提交
161 162

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

L
Len Brown 已提交
165
	for (i = 0; i < list->count; i++) {
166 167 168 169 170 171
		/*
		 * 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 已提交
172
		if (result)
173
			return result;
L
Linus Torvalds 已提交
174

175
		*state = state1;
L
Linus Torvalds 已提交
176 177 178 179 180 181

		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
{
189
	int result = 0, state;
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
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
L
Linus Torvalds 已提交
228
	if (ACPI_FAILURE(status))
229
		return -ENODEV;
L
Linus Torvalds 已提交
230

231
	result = acpi_power_get_state(resource->device->handle, &state);
L
Linus Torvalds 已提交
232
	if (result)
233
		return result;
234
	if (state != ACPI_POWER_RESOURCE_STATE_ON)
235
		return -ENOEXEC;
L
Linus Torvalds 已提交
236 237

	/* Update the power resource's _device_ power state */
238
	resource->device->power.state = ACPI_STATE_D0;
L
Linus Torvalds 已提交
239 240

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
L
Len Brown 已提交
241
			  resource->name));
242
	return 0;
L
Linus Torvalds 已提交
243 244
}

245
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
L
Linus Torvalds 已提交
246
{
247
	int result = 0, state;
L
Len Brown 已提交
248
	acpi_status status = AE_OK;
L
Linus Torvalds 已提交
249
	struct acpi_power_resource *resource = NULL;
250 251 252
	struct list_head *node, *next;
	struct acpi_power_reference *ref;

L
Linus Torvalds 已提交
253 254 255

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

258 259 260 261 262 263 264 265 266 267 268
	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 已提交
269

270 271 272 273
	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);
274
		return 0;
L
Linus Torvalds 已提交
275
	}
276
	mutex_unlock(&resource->resource_lock);
L
Linus Torvalds 已提交
277

278
	status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
L
Linus Torvalds 已提交
279
	if (ACPI_FAILURE(status))
280
		return -ENODEV;
L
Linus Torvalds 已提交
281

282
	result = acpi_power_get_state(handle, &state);
L
Linus Torvalds 已提交
283
	if (result)
284
		return result;
285
	if (state != ACPI_POWER_RESOURCE_STATE_OFF)
286
		return -ENOEXEC;
L
Linus Torvalds 已提交
287 288

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

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

294
	return 0;
L
Linus Torvalds 已提交
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 359
/**
 * 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 已提交
360 361 362
/*
 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
 * 1. Power on the power resources required for the wakeup device 
363 364
 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
 *    State Wake) for the device, if present
L
Linus Torvalds 已提交
365
 */
366
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
L
Linus Torvalds 已提交
367
{
368
	int i, err;
L
Linus Torvalds 已提交
369 370

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

373 374 375 376 377 378 379
	/*
	 * 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 已提交
380 381
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
382
		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
383
		if (ret) {
384
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
385
			dev->wakeup.flags.valid = 0;
386
			return -ENODEV;
L
Linus Torvalds 已提交
387 388 389
		}
	}

390 391 392 393
	/*
	 * Passing 3 as the third argument below means the device may be placed
	 * in arbitrary power state afterwards.
	 */
394 395 396 397 398
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
	if (!err)
		dev->wakeup.flags.prepared = 1;

	return err;
L
Linus Torvalds 已提交
399 400 401 402
}

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

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

414 415 416 417 418 419 420 421 422
	/*
	 * 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;

423 424 425
	ret = acpi_device_sleep_wake(dev, 0, 0, 0);
	if (ret)
		return ret;
L
Linus Torvalds 已提交
426 427 428

	/* Close power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
429
		ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
430
		if (ret) {
431
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
432
			dev->wakeup.flags.valid = 0;
433
			return -ENODEV;
L
Linus Torvalds 已提交
434 435 436
		}
	}

437
	return ret;
L
Linus Torvalds 已提交
438 439 440 441 442 443
}

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

L
Len Brown 已提交
444
int acpi_power_get_inferred_state(struct acpi_device *device)
L
Linus Torvalds 已提交
445
{
L
Len Brown 已提交
446 447 448 449
	int result = 0;
	struct acpi_handle_list *list = NULL;
	int list_state = 0;
	int i = 0;
L
Linus Torvalds 已提交
450 451 452


	if (!device)
453
		return -EINVAL;
L
Linus Torvalds 已提交
454 455 456 457 458 459 460

	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 已提交
461
	for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
L
Linus Torvalds 已提交
462 463 464 465 466 467
		list = &device->power.states[i].resources;
		if (list->count < 1)
			continue;

		result = acpi_power_get_list_state(list, &list_state);
		if (result)
468
			return result;
L
Linus Torvalds 已提交
469 470 471

		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
			device->power.state = i;
472
			return 0;
L
Linus Torvalds 已提交
473 474 475 476 477
		}
	}

	device->power.state = ACPI_STATE_D3;

478
	return 0;
L
Linus Torvalds 已提交
479 480
}

L
Len Brown 已提交
481
int acpi_power_transition(struct acpi_device *device, int state)
L
Linus Torvalds 已提交
482
{
L
Len Brown 已提交
483 484 485 486
	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 已提交
487 488 489


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

L
Len Brown 已提交
492 493
	if ((device->power.state < ACPI_STATE_D0)
	    || (device->power.state > ACPI_STATE_D3))
494
		return -ENODEV;
L
Linus Torvalds 已提交
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509

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

516 517 518 519
	if (device->power.state == state) {
		goto end;
	}

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

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

537
	return result;
L
Linus Torvalds 已提交
538 539 540 541 542 543
}

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

L
Len Brown 已提交
544
static struct proc_dir_entry *acpi_power_dir;
L
Linus Torvalds 已提交
545 546 547

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


555
	resource = seq->private;
L
Linus Torvalds 已提交
556 557 558 559

	if (!resource)
		goto end;

560
	result = acpi_power_get_state(resource->device->handle, &state);
561 562 563
	if (result)
		goto end;

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

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

L
Len Brown 已提交
590
      end:
591
	return 0;
L
Linus Torvalds 已提交
592 593 594 595 596 597 598
}

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


	if (!device)
605
		return -EINVAL;
L
Linus Torvalds 已提交
606 607 608

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
609
						     acpi_power_dir);
L
Linus Torvalds 已提交
610
		if (!acpi_device_dir(device))
611
			return -ENODEV;
L
Linus Torvalds 已提交
612 613 614
	}

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

L
Len Brown 已提交
623
static int acpi_power_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631 632
{

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

633
	return 0;
L
Linus Torvalds 已提交
634 635 636 637 638 639
}

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

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


	if (!device)
650
		return -EINVAL;
L
Linus Torvalds 已提交
651

652
	resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
L
Linus Torvalds 已提交
653
	if (!resource)
654
		return -ENOMEM;
L
Linus Torvalds 已提交
655

656
	resource->device = device;
657 658
	mutex_init(&resource->resource_lock);
	INIT_LIST_HEAD(&resource->reference);
L
Linus Torvalds 已提交
659 660 661 662 663 664
	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. */
665
	status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
L
Linus Torvalds 已提交
666 667 668 669 670 671 672
	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;

673
	result = acpi_power_get_state(device->handle, &state);
L
Linus Torvalds 已提交
674 675 676
	if (result)
		goto end;

677
	switch (state) {
L
Linus Torvalds 已提交
678 679 680 681 682 683 684 685 686 687 688 689 690 691
	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 已提交
692

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

L
Len Brown 已提交
696
      end:
L
Linus Torvalds 已提交
697 698
	if (result)
		kfree(resource);
L
Len Brown 已提交
699

700
	return result;
L
Linus Torvalds 已提交
701 702
}

L
Len Brown 已提交
703
static int acpi_power_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
704 705
{
	struct acpi_power_resource *resource = NULL;
706
	struct list_head *node, *next;
L
Linus Torvalds 已提交
707 708 709


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

712
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
713 714 715

	acpi_power_remove_fs(device);

716 717 718 719 720 721 722 723
	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 已提交
724 725
	kfree(resource);

726
	return 0;
L
Linus Torvalds 已提交
727 728
}

729
static int acpi_power_resume(struct acpi_device *device)
730
{
731
	int result = 0, state;
732 733 734 735 736 737 738 739
	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);

740
	result = acpi_power_get_state(device->handle, &state);
741 742 743 744
	if (result)
		return result;

	mutex_lock(&resource->resource_lock);
745
	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
746 747 748 749 750 751 752 753 754 755 756
	    !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 已提交
757
static int __init acpi_power_init(void)
L
Linus Torvalds 已提交
758
{
L
Len Brown 已提交
759
	int result = 0;
L
Linus Torvalds 已提交
760 761 762


	if (acpi_disabled)
763
		return 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 779 780
}

subsys_initcall(acpi_power_init);