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 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(struct acpi_power_resource *resource, 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 (!resource || !state)
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
	*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
			  resource->name, 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
Linus Torvalds 已提交
156
	struct acpi_power_resource *resource = NULL;
L
Len Brown 已提交
157
	u32 i = 0;
L
Linus Torvalds 已提交
158 159 160


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

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

L
Len Brown 已提交
165
	for (i = 0; i < list->count; i++) {
L
Linus Torvalds 已提交
166 167
		result = acpi_power_get_context(list->handles[i], &resource);
		if (result)
168
			return result;
169
		result = acpi_power_get_state(resource, &state1);
L
Linus Torvalds 已提交
170
		if (result)
171
			return result;
L
Linus Torvalds 已提交
172

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

		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
			break;
	}

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

182
	return result;
L
Linus Torvalds 已提交
183 184
}

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


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

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

225
	status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
L
Linus Torvalds 已提交
226
	if (ACPI_FAILURE(status))
227
		return -ENODEV;
L
Linus Torvalds 已提交
228

229
	result = acpi_power_get_state(resource, &state);
L
Linus Torvalds 已提交
230
	if (result)
231
		return result;
232
	if (state != ACPI_POWER_RESOURCE_STATE_ON)
233
		return -ENOEXEC;
L
Linus Torvalds 已提交
234 235

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

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

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

L
Linus Torvalds 已提交
251 252 253

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

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

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

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

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

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

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

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

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

371 372 373 374 375 376 377
	/*
	 * 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 已提交
378 379
	/* Open power resource */
	for (i = 0; i < dev->wakeup.resources.count; i++) {
380
		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
L
Linus Torvalds 已提交
381
		if (ret) {
382
			printk(KERN_ERR PREFIX "Transition power state\n");
L
Linus Torvalds 已提交
383
			dev->wakeup.flags.valid = 0;
384
			return -ENODEV;
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 394 395 396
	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
	if (!err)
		dev->wakeup.flags.prepared = 1;

	return err;
L
Linus Torvalds 已提交
397 398 399 400
}

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

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

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

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

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

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

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

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


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

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

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

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

	device->power.state = ACPI_STATE_D3;

476
	return 0;
L
Linus Torvalds 已提交
477 478
}

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


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

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

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

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

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

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

535
	return result;
L
Linus Torvalds 已提交
536 537 538 539 540 541
}

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

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

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


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

	if (!resource)
		goto end;

558
	result = acpi_power_get_state(resource, &state);
559 560 561
	if (result)
		goto end;

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

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

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

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


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

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

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

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

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

631
	return 0;
L
Linus Torvalds 已提交
632 633 634 635 636 637
}

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

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


	if (!device)
648
		return -EINVAL;
L
Linus Torvalds 已提交
649

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

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

671
	result = acpi_power_get_state(resource, &state);
L
Linus Torvalds 已提交
672 673 674
	if (result)
		goto end;

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

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

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

698
	return result;
L
Linus Torvalds 已提交
699 700
}

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


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

710
	resource = acpi_driver_data(device);
L
Linus Torvalds 已提交
711 712 713

	acpi_power_remove_fs(device);

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

724
	return 0;
L
Linus Torvalds 已提交
725 726
}

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

738
	result = acpi_power_get_state(resource, &state);
739 740 741 742
	if (result)
		return result;

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


	if (acpi_disabled)
761
		return 0;
L
Linus Torvalds 已提交
762 763 764 765 766

	INIT_LIST_HEAD(&acpi_power_resource_list);

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

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

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

subsys_initcall(acpi_power_init);