1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * ec_sys.c
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2010 SUSE Products GmbH/Novell
6*4882a593Smuzhiyun * Author:
7*4882a593Smuzhiyun * Thomas Renninger <trenn@suse.de>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/kernel.h>
11*4882a593Smuzhiyun #include <linux/acpi.h>
12*4882a593Smuzhiyun #include <linux/debugfs.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/uaccess.h>
15*4882a593Smuzhiyun #include "internal.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
18*4882a593Smuzhiyun MODULE_DESCRIPTION("ACPI EC sysfs access driver");
19*4882a593Smuzhiyun MODULE_LICENSE("GPL");
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun static bool write_support;
22*4882a593Smuzhiyun module_param(write_support, bool, 0644);
23*4882a593Smuzhiyun MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may "
24*4882a593Smuzhiyun "be needed.");
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #define EC_SPACE_SIZE 256
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static struct dentry *acpi_ec_debugfs_dir;
29*4882a593Smuzhiyun
acpi_ec_read_io(struct file * f,char __user * buf,size_t count,loff_t * off)30*4882a593Smuzhiyun static ssize_t acpi_ec_read_io(struct file *f, char __user *buf,
31*4882a593Smuzhiyun size_t count, loff_t *off)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun /* Use this if support reading/writing multiple ECs exists in ec.c:
34*4882a593Smuzhiyun * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
35*4882a593Smuzhiyun */
36*4882a593Smuzhiyun unsigned int size = EC_SPACE_SIZE;
37*4882a593Smuzhiyun loff_t init_off = *off;
38*4882a593Smuzhiyun int err = 0;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (*off >= size)
41*4882a593Smuzhiyun return 0;
42*4882a593Smuzhiyun if (*off + count >= size) {
43*4882a593Smuzhiyun size -= *off;
44*4882a593Smuzhiyun count = size;
45*4882a593Smuzhiyun } else
46*4882a593Smuzhiyun size = count;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun while (size) {
49*4882a593Smuzhiyun u8 byte_read;
50*4882a593Smuzhiyun err = ec_read(*off, &byte_read);
51*4882a593Smuzhiyun if (err)
52*4882a593Smuzhiyun return err;
53*4882a593Smuzhiyun if (put_user(byte_read, buf + *off - init_off)) {
54*4882a593Smuzhiyun if (*off - init_off)
55*4882a593Smuzhiyun return *off - init_off; /* partial read */
56*4882a593Smuzhiyun return -EFAULT;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun *off += 1;
59*4882a593Smuzhiyun size--;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun return count;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
acpi_ec_write_io(struct file * f,const char __user * buf,size_t count,loff_t * off)64*4882a593Smuzhiyun static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
65*4882a593Smuzhiyun size_t count, loff_t *off)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun /* Use this if support reading/writing multiple ECs exists in ec.c:
68*4882a593Smuzhiyun * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private;
69*4882a593Smuzhiyun */
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun unsigned int size = count;
72*4882a593Smuzhiyun loff_t init_off = *off;
73*4882a593Smuzhiyun int err = 0;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun if (!write_support)
76*4882a593Smuzhiyun return -EINVAL;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun if (*off >= EC_SPACE_SIZE)
79*4882a593Smuzhiyun return 0;
80*4882a593Smuzhiyun if (*off + count >= EC_SPACE_SIZE) {
81*4882a593Smuzhiyun size = EC_SPACE_SIZE - *off;
82*4882a593Smuzhiyun count = size;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun while (size) {
86*4882a593Smuzhiyun u8 byte_write;
87*4882a593Smuzhiyun if (get_user(byte_write, buf + *off - init_off)) {
88*4882a593Smuzhiyun if (*off - init_off)
89*4882a593Smuzhiyun return *off - init_off; /* partial write */
90*4882a593Smuzhiyun return -EFAULT;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun err = ec_write(*off, byte_write);
93*4882a593Smuzhiyun if (err)
94*4882a593Smuzhiyun return err;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun *off += 1;
97*4882a593Smuzhiyun size--;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun return count;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun static const struct file_operations acpi_ec_io_ops = {
103*4882a593Smuzhiyun .owner = THIS_MODULE,
104*4882a593Smuzhiyun .open = simple_open,
105*4882a593Smuzhiyun .read = acpi_ec_read_io,
106*4882a593Smuzhiyun .write = acpi_ec_write_io,
107*4882a593Smuzhiyun .llseek = default_llseek,
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun
acpi_ec_add_debugfs(struct acpi_ec * ec,unsigned int ec_device_count)110*4882a593Smuzhiyun static void acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun struct dentry *dev_dir;
113*4882a593Smuzhiyun char name[64];
114*4882a593Smuzhiyun umode_t mode = 0400;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (ec_device_count == 0)
117*4882a593Smuzhiyun acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun sprintf(name, "ec%u", ec_device_count);
120*4882a593Smuzhiyun dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun debugfs_create_x32("gpe", 0444, dev_dir, &first_ec->gpe);
123*4882a593Smuzhiyun debugfs_create_bool("use_global_lock", 0444, dev_dir,
124*4882a593Smuzhiyun &first_ec->global_lock);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (write_support)
127*4882a593Smuzhiyun mode = 0600;
128*4882a593Smuzhiyun debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
acpi_ec_sys_init(void)131*4882a593Smuzhiyun static int __init acpi_ec_sys_init(void)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun if (first_ec)
134*4882a593Smuzhiyun acpi_ec_add_debugfs(first_ec, 0);
135*4882a593Smuzhiyun return 0;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
acpi_ec_sys_exit(void)138*4882a593Smuzhiyun static void __exit acpi_ec_sys_exit(void)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun debugfs_remove_recursive(acpi_ec_debugfs_dir);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun module_init(acpi_ec_sys_init);
144*4882a593Smuzhiyun module_exit(acpi_ec_sys_exit);
145