xref: /linux/drivers/acpi/ec_sys.c (revision 9827886dce77c47c378ce3154689cea2c45c731d)
1*9827886dSThomas Renninger /*
2*9827886dSThomas Renninger  * ec_sys.c
3*9827886dSThomas Renninger  *
4*9827886dSThomas Renninger  * Copyright (C) 2010 SUSE Products GmbH/Novell
5*9827886dSThomas Renninger  * Author:
6*9827886dSThomas Renninger  *      Thomas Renninger <trenn@suse.de>
7*9827886dSThomas Renninger  *
8*9827886dSThomas Renninger  * This work is licensed under the terms of the GNU GPL, version 2.
9*9827886dSThomas Renninger  */
10*9827886dSThomas Renninger 
111195a098SThomas Renninger #include <linux/kernel.h>
121195a098SThomas Renninger #include <linux/acpi.h>
131195a098SThomas Renninger #include <linux/debugfs.h>
141195a098SThomas Renninger #include "internal.h"
151195a098SThomas Renninger 
161195a098SThomas Renninger MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
171195a098SThomas Renninger MODULE_DESCRIPTION("ACPI EC sysfs access driver");
181195a098SThomas Renninger MODULE_LICENSE("GPL");
191195a098SThomas Renninger 
20*9827886dSThomas Renninger #define EC_SPACE_SIZE 256
21*9827886dSThomas Renninger 
221195a098SThomas Renninger struct sysdev_class acpi_ec_sysdev_class = {
231195a098SThomas Renninger 	.name = "ec",
241195a098SThomas Renninger };
251195a098SThomas Renninger 
261195a098SThomas Renninger static struct dentry *acpi_ec_debugfs_dir;
271195a098SThomas Renninger 
28*9827886dSThomas Renninger static int acpi_ec_open_io(struct inode *i, struct file *f)
29*9827886dSThomas Renninger {
30*9827886dSThomas Renninger 	f->private_data = i->i_private;
31*9827886dSThomas Renninger 	return 0;
32*9827886dSThomas Renninger }
33*9827886dSThomas Renninger 
34*9827886dSThomas Renninger static ssize_t acpi_ec_read_io(struct file *f, char __user *buf,
35*9827886dSThomas Renninger 			       size_t count, loff_t *off)
36*9827886dSThomas Renninger {
37*9827886dSThomas Renninger 	/* Use this if support reading/writing multiple ECs exists in ec.c:
38*9827886dSThomas Renninger 	 * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
39*9827886dSThomas Renninger 	 */
40*9827886dSThomas Renninger 	unsigned int size = EC_SPACE_SIZE;
41*9827886dSThomas Renninger 	u8 *data = (u8 *) buf;
42*9827886dSThomas Renninger 	loff_t init_off = *off;
43*9827886dSThomas Renninger 	int err = 0;
44*9827886dSThomas Renninger 
45*9827886dSThomas Renninger 	if (*off >= size)
46*9827886dSThomas Renninger 		return 0;
47*9827886dSThomas Renninger 	if (*off + count >= size) {
48*9827886dSThomas Renninger 		size -= *off;
49*9827886dSThomas Renninger 		count = size;
50*9827886dSThomas Renninger 	} else
51*9827886dSThomas Renninger 		size = count;
52*9827886dSThomas Renninger 
53*9827886dSThomas Renninger 	while (size) {
54*9827886dSThomas Renninger 		err = ec_read(*off, &data[*off - init_off]);
55*9827886dSThomas Renninger 		if (err)
56*9827886dSThomas Renninger 			return err;
57*9827886dSThomas Renninger 		*off += 1;
58*9827886dSThomas Renninger 		size--;
59*9827886dSThomas Renninger 	}
60*9827886dSThomas Renninger 	return count;
61*9827886dSThomas Renninger }
62*9827886dSThomas Renninger 
63*9827886dSThomas Renninger static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
64*9827886dSThomas Renninger 				size_t count, loff_t *off)
65*9827886dSThomas Renninger {
66*9827886dSThomas Renninger 	/* Use this if support reading/writing multiple ECs exists in ec.c:
67*9827886dSThomas Renninger 	 * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
68*9827886dSThomas Renninger 	 */
69*9827886dSThomas Renninger 
70*9827886dSThomas Renninger 	unsigned int size = count;
71*9827886dSThomas Renninger 	loff_t init_off = *off;
72*9827886dSThomas Renninger 	u8 *data = (u8 *) buf;
73*9827886dSThomas Renninger 	int err = 0;
74*9827886dSThomas Renninger 
75*9827886dSThomas Renninger 	if (*off >= EC_SPACE_SIZE)
76*9827886dSThomas Renninger 		return 0;
77*9827886dSThomas Renninger 	if (*off + count >= EC_SPACE_SIZE) {
78*9827886dSThomas Renninger 		size = EC_SPACE_SIZE - *off;
79*9827886dSThomas Renninger 		count = size;
80*9827886dSThomas Renninger 	}
81*9827886dSThomas Renninger 
82*9827886dSThomas Renninger 	while (size) {
83*9827886dSThomas Renninger 		u8 byte_write = data[*off - init_off];
84*9827886dSThomas Renninger 		err = ec_write(*off, byte_write);
85*9827886dSThomas Renninger 		if (err)
86*9827886dSThomas Renninger 			return err;
87*9827886dSThomas Renninger 
88*9827886dSThomas Renninger 		*off += 1;
89*9827886dSThomas Renninger 		size--;
90*9827886dSThomas Renninger 	}
91*9827886dSThomas Renninger 	return count;
92*9827886dSThomas Renninger }
93*9827886dSThomas Renninger 
94*9827886dSThomas Renninger static struct file_operations acpi_ec_io_ops = {
95*9827886dSThomas Renninger 	.owner = THIS_MODULE,
96*9827886dSThomas Renninger 	.open  = acpi_ec_open_io,
97*9827886dSThomas Renninger 	.read  = acpi_ec_read_io,
98*9827886dSThomas Renninger 	.write = acpi_ec_write_io,
99*9827886dSThomas Renninger };
100*9827886dSThomas Renninger 
1011195a098SThomas Renninger int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
1021195a098SThomas Renninger {
1031195a098SThomas Renninger 	struct dentry *dev_dir;
1041195a098SThomas Renninger 	char name[64];
1051195a098SThomas Renninger 	if (ec_device_count == 0) {
1061195a098SThomas Renninger 		acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
1071195a098SThomas Renninger 		if (!acpi_ec_debugfs_dir)
1081195a098SThomas Renninger 			return -ENOMEM;
1091195a098SThomas Renninger 	}
1101195a098SThomas Renninger 
1111195a098SThomas Renninger 	sprintf(name, "ec%u", ec_device_count);
1121195a098SThomas Renninger 	dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
1131195a098SThomas Renninger 	if (!dev_dir) {
1141195a098SThomas Renninger 		if (ec_device_count == 0)
1151195a098SThomas Renninger 			debugfs_remove_recursive(acpi_ec_debugfs_dir);
1161195a098SThomas Renninger 		/* TBD: Proper cleanup for multiple ECs */
1171195a098SThomas Renninger 		return -ENOMEM;
1181195a098SThomas Renninger 	}
1191195a098SThomas Renninger 
1201195a098SThomas Renninger 	debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe);
1211195a098SThomas Renninger 	debugfs_create_bool("use_global_lock", 0444, dev_dir,
1221195a098SThomas Renninger 			    (u32 *)&first_ec->global_lock);
123*9827886dSThomas Renninger 	debugfs_create_file("io", 0666, dev_dir, ec, &acpi_ec_io_ops);
1241195a098SThomas Renninger 	return 0;
1251195a098SThomas Renninger }
1261195a098SThomas Renninger 
1271195a098SThomas Renninger static int __init acpi_ec_sys_init(void)
1281195a098SThomas Renninger {
1291195a098SThomas Renninger 	int err = 0;
1301195a098SThomas Renninger 	if (first_ec)
1311195a098SThomas Renninger 		err = acpi_ec_add_debugfs(first_ec, 0);
1321195a098SThomas Renninger 	else
1331195a098SThomas Renninger 		err = -ENODEV;
1341195a098SThomas Renninger 	return err;
1351195a098SThomas Renninger }
1361195a098SThomas Renninger 
1371195a098SThomas Renninger static void __exit acpi_ec_sys_exit(void)
1381195a098SThomas Renninger {
1391195a098SThomas Renninger 	debugfs_remove_recursive(acpi_ec_debugfs_dir);
1401195a098SThomas Renninger }
1411195a098SThomas Renninger 
1421195a098SThomas Renninger module_init(acpi_ec_sys_init);
1431195a098SThomas Renninger module_exit(acpi_ec_sys_exit);
144