system.c 14.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
/*
 *  acpi_system.c - ACPI System Driver ($Revision: 63 $)
 *
 *  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.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
29
#include <linux/string.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
31
32
33
34
#include <asm/uaccess.h>

#include <acpi/acpi_drivers.h>

#define _COMPONENT		ACPI_SYSTEM_COMPONENT
35
ACPI_MODULE_NAME("system");
Zhang Rui's avatar
Zhang Rui committed
36
37
38
39
40
#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
#define MODULE_PARAM_PREFIX "acpi."

Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
#define ACPI_SYSTEM_CLASS		"system"
#define ACPI_SYSTEM_DEVICE_NAME		"System"

44
45
u32 acpi_irq_handled;

Zhang Rui's avatar
Zhang Rui committed
46
47
48
/*
 * Make ACPICA version work as module param
 */
49
50
static int param_get_acpica_version(char *buffer, struct kernel_param *kp)
{
Zhang Rui's avatar
Zhang Rui committed
51
52
53
54
55
56
57
58
59
	int result;

	result = sprintf(buffer, "%x", ACPI_CA_VERSION);

	return result;
}

module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);

60
61
62
63
/* --------------------------------------------------------------------------
                              FS Interface (/sys)
   -------------------------------------------------------------------------- */
static LIST_HEAD(acpi_table_attr_list);
64
static struct kobject *tables_kobj;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

struct acpi_table_attr {
	struct bin_attribute attr;
	char name[8];
	int instance;
	struct list_head node;
};

static ssize_t acpi_table_show(struct kobject *kobj,
			       struct bin_attribute *bin_attr, char *buf,
			       loff_t offset, size_t count)
{
	struct acpi_table_attr *table_attr =
	    container_of(bin_attr, struct acpi_table_attr, attr);
	struct acpi_table_header *table_header = NULL;
	acpi_status status;
81
82
83
84
85
86
	char name[ACPI_NAME_SIZE];

	if (strncmp(table_attr->name, "NULL", 4))
		memcpy(name, table_attr->name, ACPI_NAME_SIZE);
	else
		memcpy(name, "\0\0\0\0", 4);
87
88

	status =
89
	    acpi_get_table(name, table_attr->instance,
90
91
92
93
			   &table_header);
	if (ACPI_FAILURE(status))
		return -ENODEV;

94
95
	return memory_read_from_buffer(buf, count, &offset,
					table_header, table_header->length);
96
97
98
99
100
101
102
103
}

static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
				 struct acpi_table_header *table_header)
{
	struct acpi_table_header *header = NULL;
	struct acpi_table_attr *attr = NULL;

104
105
106
107
108
	if (table_header->signature[0] != '\0')
		memcpy(table_attr->name, table_header->signature,
			ACPI_NAME_SIZE);
	else
		memcpy(table_attr->name, "NULL", 4);
109
110

	list_for_each_entry(attr, &acpi_table_attr_list, node) {
111
		if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE))
112
113
114
115
116
117
			if (table_attr->instance < attr->instance)
				table_attr->instance = attr->instance;
	}
	table_attr->instance++;

	if (table_attr->instance > 1 || (table_attr->instance == 1 &&
118
119
120
121
					!acpi_get_table
					(table_header->signature, 2, &header)))
		sprintf(table_attr->name + ACPI_NAME_SIZE, "%d",
			table_attr->instance);
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

	table_attr->attr.size = 0;
	table_attr->attr.read = acpi_table_show;
	table_attr->attr.attr.name = table_attr->name;
	table_attr->attr.attr.mode = 0444;

	return;
}

static int acpi_system_sysfs_init(void)
{
	struct acpi_table_attr *table_attr;
	struct acpi_table_header *table_header = NULL;
	int table_index = 0;
	int result;

138
139
140
	tables_kobj = kobject_create_and_add("tables", acpi_kobj);
	if (!tables_kobj)
		return -ENOMEM;
141
142
143
144
145
146
147
148
149
150
151
152
153

	do {
		result = acpi_get_table_by_index(table_index, &table_header);
		if (!result) {
			table_index++;
			table_attr = NULL;
			table_attr =
			    kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL);
			if (!table_attr)
				return -ENOMEM;

			acpi_table_attr_init(table_attr, table_header);
			result =
154
			    sysfs_create_bin_file(tables_kobj,
155
156
157
158
159
160
161
162
163
						  &table_attr->attr);
			if (result) {
				kfree(table_attr);
				return result;
			} else
				list_add_tail(&table_attr->node,
					      &acpi_table_attr_list);
		}
	} while (!result);
164
	kobject_uevent(tables_kobj, KOBJ_ADD);
165
166
167
168

	return 0;
}

169
170
171
172
173
174
175
176
177
178
/*
 * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/
 * See Documentation/ABI/testing/sysfs-firmware-acpi
 */

#define COUNT_GPE 0
#define COUNT_SCI 1	/* acpi_irq_handled */
#define COUNT_ERROR 2	/* other */
#define NUM_COUNTERS_EXTRA 3

179
180
181
182
183
184
struct event_counter {
	u32 count;
	u32 flags;
};

static struct event_counter *all_counters;
185
186
187
188
189
190
191
192
193
194
195
196
static u32 num_gpes;
static u32 num_counters;
static struct attribute **all_attrs;
static u32 acpi_gpe_count;

static struct attribute_group interrupt_stats_attr_group = {
	.name = "interrupts",
};
static struct kobj_attribute *counter_attrs;

static void delete_gpe_attr_array(void)
{
197
	struct event_counter *tmp = all_counters;
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

	all_counters = NULL;
	kfree(tmp);

	if (counter_attrs) {
		int i;

		for (i = 0; i < num_gpes; i++)
			kfree(counter_attrs[i].attr.name);

		kfree(counter_attrs);
	}
	kfree(all_attrs);

	return;
}

void acpi_os_gpe_count(u32 gpe_number)
{
	acpi_gpe_count++;

	if (!all_counters)
		return;

	if (gpe_number < num_gpes)
223
		all_counters[gpe_number].count++;
224
	else
225
226
		all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR].
					count++;
227
228
229
230
231
232
233
234
235
236

	return;
}

void acpi_os_fixed_event_count(u32 event_number)
{
	if (!all_counters)
		return;

	if (event_number < ACPI_NUM_FIXED_EVENTS)
237
		all_counters[num_gpes + event_number].count++;
238
	else
239
240
		all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR].
				count++;
241
242
243
244

	return;
}

245
246
247
248
249
250
251
252
static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle)
{
	int result = 0;

	if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
		goto end;

	if (index < num_gpes) {
253
		result = acpi_get_gpe_device(index, handle);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
		if (result) {
			ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND,
				"Invalid GPE 0x%x\n", index));
			goto end;
		}
		result = acpi_get_gpe_status(*handle, index,
						ACPI_NOT_ISR, status);
	} else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS))
		result = acpi_get_event_status(index - num_gpes, status);

end:
	return result;
}

268
269
270
static ssize_t counter_show(struct kobject *kobj,
	struct kobj_attribute *attr, char *buf)
{
271
272
273
274
275
276
277
	int index = attr - counter_attrs;
	int size;
	acpi_handle handle;
	acpi_event_status status;
	int result = 0;

	all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count =
278
		acpi_irq_handled;
279
	all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count =
280
281
		acpi_gpe_count;

282
283
284
285
286
287
288
289
290
291
	size = sprintf(buf, "%8d", all_counters[index].count);

	/* "gpe_all" or "sci" */
	if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
		goto end;

	result = get_status(index, &status, &handle);
	if (result)
		goto end;

292
293
	if (!(status & ACPI_EVENT_FLAG_HANDLE))
		size += sprintf(buf + size, "	invalid");
294
	else if (status & ACPI_EVENT_FLAG_ENABLED)
295
296
297
		size += sprintf(buf + size, "	enabled");
	else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
		size += sprintf(buf + size, "	wake_enabled");
298
	else
299
		size += sprintf(buf + size, "	disabled");
300
301
302
303

end:
	size += sprintf(buf + size, "\n");
	return result ? result : size;
304
305
306
307
308
}

/*
 * counter_set() sets the specified counter.
 * setting the total "sci" file to any value clears all counters.
309
 * enable/disable/clear a gpe/fixed event in user space.
310
311
312
313
314
 */
static ssize_t counter_set(struct kobject *kobj,
	struct kobj_attribute *attr, const char *buf, size_t size)
{
	int index = attr - counter_attrs;
315
316
317
	acpi_event_status status;
	acpi_handle handle;
	int result = 0;
318
319
320
321

	if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {
		int i;
		for (i = 0; i < num_counters; ++i)
322
			all_counters[i].count = 0;
323
324
		acpi_gpe_count = 0;
		acpi_irq_handled = 0;
325
326
327
328
329
330
331
332
		goto end;
	}

	/* show the event status for both GPEs and Fixed Events */
	result = get_status(index, &status, &handle);
	if (result)
		goto end;

333
	if (!(status & ACPI_EVENT_FLAG_HANDLE)) {
334
335
		printk(KERN_WARNING PREFIX
			"Can not change Invalid GPE/Fixed Event status\n");
336
337
		return -EINVAL;
	}
338

339
340
341
	if (index < num_gpes) {
		if (!strcmp(buf, "disable\n") &&
				(status & ACPI_EVENT_FLAG_ENABLED))
342
			result = acpi_disable_gpe(handle, index);
343
344
		else if (!strcmp(buf, "enable\n") &&
				!(status & ACPI_EVENT_FLAG_ENABLED))
345
			result = acpi_enable_gpe(handle, index);
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
		else if (!strcmp(buf, "clear\n") &&
				(status & ACPI_EVENT_FLAG_SET))
			result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR);
		else
			all_counters[index].count = strtoul(buf, NULL, 0);
	} else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {
		int event = index - num_gpes;
		if (!strcmp(buf, "disable\n") &&
				(status & ACPI_EVENT_FLAG_ENABLED))
			result = acpi_disable_event(event, ACPI_NOT_ISR);
		else if (!strcmp(buf, "enable\n") &&
				!(status & ACPI_EVENT_FLAG_ENABLED))
			result = acpi_enable_event(event, ACPI_NOT_ISR);
		else if (!strcmp(buf, "clear\n") &&
				(status & ACPI_EVENT_FLAG_SET))
			result = acpi_clear_event(event);
		else
			all_counters[index].count = strtoul(buf, NULL, 0);
364
	} else
365
		all_counters[index].count = strtoul(buf, NULL, 0);
366

367
368
369
370
	if (ACPI_FAILURE(result))
		result = -EINVAL;
end:
	return result ? result : size;
371
372
373
374
375
376
377
378
379
}

void acpi_irq_stats_init(void)
{
	int i;

	if (all_counters)
		return;

380
	num_gpes = acpi_current_gpe_count;
381
382
383
384
385
386
387
	num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;

	all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1),
			GFP_KERNEL);
	if (all_attrs == NULL)
		return;

388
389
	all_counters = kzalloc(sizeof(struct event_counter) * (num_counters),
				GFP_KERNEL);
390
391
392
393
394
395
396
397
398
	if (all_counters == NULL)
		goto fail;

	counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters),
			GFP_KERNEL);
	if (counter_attrs == NULL)
		goto fail;

	for (i = 0; i < num_counters; ++i) {
399
		char buffer[12];
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
		char *name;

		if (i < num_gpes)
			sprintf(buffer, "gpe%02X", i);
		else if (i == num_gpes + ACPI_EVENT_PMTIMER)
			sprintf(buffer, "ff_pmtimer");
		else if (i == num_gpes + ACPI_EVENT_GLOBAL)
			sprintf(buffer, "ff_gbl_lock");
		else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)
			sprintf(buffer, "ff_pwr_btn");
		else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)
			sprintf(buffer, "ff_slp_btn");
		else if (i == num_gpes + ACPI_EVENT_RTC)
			sprintf(buffer, "ff_rt_clk");
		else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)
			sprintf(buffer, "gpe_all");
		else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)
			sprintf(buffer, "sci");
		else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)
			sprintf(buffer, "error");
		else
			sprintf(buffer, "bug%02X", i);

		name = kzalloc(strlen(buffer) + 1, GFP_KERNEL);
		if (name == NULL)
			goto fail;
		strncpy(name, buffer, strlen(buffer) + 1);

		counter_attrs[i].attr.name = name;
		counter_attrs[i].attr.mode = 0644;
		counter_attrs[i].show = counter_show;
		counter_attrs[i].store = counter_set;

		all_attrs[i] = &counter_attrs[i].attr;
	}

	interrupt_stats_attr_group.attrs = all_attrs;
Len Brown's avatar
Len Brown committed
437
438
	if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group))
		return;
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

fail:
	delete_gpe_attr_array();
	return;
}

static void __exit interrupt_stats_exit(void)
{
	sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);

	delete_gpe_attr_array();

	return;
}

Linus Torvalds's avatar
Linus Torvalds committed
454
455
456
/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */
Zhang Rui's avatar
Zhang Rui committed
457
#ifdef CONFIG_ACPI_PROCFS
458
459
460
461
#define ACPI_SYSTEM_FILE_INFO		"info"
#define ACPI_SYSTEM_FILE_EVENT		"event"
#define ACPI_SYSTEM_FILE_DSDT		"dsdt"
#define ACPI_SYSTEM_FILE_FADT		"fadt"
Linus Torvalds's avatar
Linus Torvalds committed
462

Len Brown's avatar
Len Brown committed
463
static int acpi_system_read_info(struct seq_file *seq, void *offset)
Linus Torvalds's avatar
Linus Torvalds committed
464
465
466
{

	seq_printf(seq, "version:                 %x\n", ACPI_CA_VERSION);
467
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
468
469
470
471
472
473
474
}

static int acpi_system_info_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_system_read_info, PDE(inode)->data);
}

475
static const struct file_operations acpi_system_info_ops = {
476
	.owner = THIS_MODULE,
Len Brown's avatar
Len Brown committed
477
478
479
480
	.open = acpi_system_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
Linus Torvalds's avatar
Linus Torvalds committed
481
482
};

Len Brown's avatar
Len Brown committed
483
484
static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t,
				     loff_t *);
Linus Torvalds's avatar
Linus Torvalds committed
485

486
static const struct file_operations acpi_system_dsdt_ops = {
487
	.owner = THIS_MODULE,
Len Brown's avatar
Len Brown committed
488
	.read = acpi_system_read_dsdt,
Linus Torvalds's avatar
Linus Torvalds committed
489
490
491
};

static ssize_t
Len Brown's avatar
Len Brown committed
492
493
acpi_system_read_dsdt(struct file *file,
		      char __user * buffer, size_t count, loff_t * ppos)
Linus Torvalds's avatar
Linus Torvalds committed
494
{
Len Brown's avatar
Len Brown committed
495
	acpi_status status = AE_OK;
496
	struct acpi_table_header *dsdt = NULL;
Len Brown's avatar
Len Brown committed
497
	ssize_t res;
Linus Torvalds's avatar
Linus Torvalds committed
498

499
	status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt);
Linus Torvalds's avatar
Linus Torvalds committed
500
	if (ACPI_FAILURE(status))
501
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
502

503
	res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length);
Linus Torvalds's avatar
Linus Torvalds committed
504

505
	return res;
Linus Torvalds's avatar
Linus Torvalds committed
506
507
}

Len Brown's avatar
Len Brown committed
508
509
static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t,
				     loff_t *);
Linus Torvalds's avatar
Linus Torvalds committed
510

511
static const struct file_operations acpi_system_fadt_ops = {
512
	.owner = THIS_MODULE,
Len Brown's avatar
Len Brown committed
513
	.read = acpi_system_read_fadt,
Linus Torvalds's avatar
Linus Torvalds committed
514
515
516
};

static ssize_t
Len Brown's avatar
Len Brown committed
517
518
acpi_system_read_fadt(struct file *file,
		      char __user * buffer, size_t count, loff_t * ppos)
Linus Torvalds's avatar
Linus Torvalds committed
519
{
Len Brown's avatar
Len Brown committed
520
	acpi_status status = AE_OK;
521
	struct acpi_table_header *fadt = NULL;
Len Brown's avatar
Len Brown committed
522
	ssize_t res;
Linus Torvalds's avatar
Linus Torvalds committed
523

524
	status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt);
Linus Torvalds's avatar
Linus Torvalds committed
525
	if (ACPI_FAILURE(status))
526
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
527

528
	res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length);
Linus Torvalds's avatar
Linus Torvalds committed
529

530
	return res;
Linus Torvalds's avatar
Linus Torvalds committed
531
532
}

533
static int acpi_system_procfs_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
534
{
Len Brown's avatar
Len Brown committed
535
	struct proc_dir_entry *entry;
Linus Torvalds's avatar
Linus Torvalds committed
536
537
538
	int error = 0;

	/* 'info' [R] */
539
540
	entry = proc_create(ACPI_SYSTEM_FILE_INFO, S_IRUGO, acpi_root_dir,
			    &acpi_system_info_ops);
Linus Torvalds's avatar
Linus Torvalds committed
541
542
543
544
	if (!entry)
		goto Error;

	/* 'dsdt' [R] */
545
546
547
	entry = proc_create(ACPI_SYSTEM_FILE_DSDT, S_IRUSR, acpi_root_dir,
			    &acpi_system_dsdt_ops);
	if (!entry)
Linus Torvalds's avatar
Linus Torvalds committed
548
549
550
		goto Error;

	/* 'fadt' [R] */
551
552
553
	entry = proc_create(ACPI_SYSTEM_FILE_FADT, S_IRUSR, acpi_root_dir,
			    &acpi_system_fadt_ops);
	if (!entry)
Linus Torvalds's avatar
Linus Torvalds committed
554
555
		goto Error;

Len Brown's avatar
Len Brown committed
556
      Done:
557
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
558

Len Brown's avatar
Len Brown committed
559
      Error:
Linus Torvalds's avatar
Linus Torvalds committed
560
561
562
563
564
565
566
	remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir);
	remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir);
	remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir);

	error = -EFAULT;
	goto Done;
}
567
568
569
570
571
572
573
#else
static int acpi_system_procfs_init(void)
{
	return 0;
}
#endif

574
int __init acpi_system_init(void)
575
{
576
	int result;
577
578
579
580
581
582
583
584
585

	result = acpi_system_procfs_init();
	if (result)
		return result;

	result = acpi_system_sysfs_init();

	return result;
}