diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 13:44:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 13:44:06 -0400 |
commit | c145307a110c14d09d5d92ff3c49dc0940e44b80 (patch) | |
tree | cba923818dea8857022de06ffd94ec6b2967aa1f /drivers/acpi | |
parent | 5e83f6fbdb020b70c0e413312801424d13c58d68 (diff) | |
parent | 1a14703d6b20010401ca273ac1f07bff7992aa2c (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (88 commits)
ips driver: make it less chatty
intel_scu_ipc: fix size field for intel_scu_ipc_command
intel_scu_ipc: return -EIO for error condition in busy_loop
intel_scu_ipc: fix data packing of PMIC command on Moorestown
Clean up command packing on MRST.
zero the stack buffer before giving random garbage to the SCU
Fix stack buffer size for IPC writev messages
intel_scu_ipc: Use the new cpu identification function
intel_scu_ipc: tidy up unused bits
Remove indirect read write api support.
intel_scu_ipc: Support Medfield processors
intel_scu_ipc: detect CPU type automatically
x86 plat: limit x86 platform driver menu to X86
acpi ec_sys: Be more cautious about ec write access
acpi ec: Fix possible double io port registration
hp-wmi: acpi_drivers.h is already included through acpi.h two lines below
hp-wmi: Fix mixing up of and/or directive
dell-laptop: make dell_laptop_i8042_filter() static
asus-laptop: fix asus_input_init error path
msi-wmi: make needlessly global symbols static
...
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 18 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 107 | ||||
-rw-r--r-- | drivers/acpi/ec_sys.c | 160 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 24 |
5 files changed, 217 insertions, 93 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 746411518802..08e0140920e1 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -104,6 +104,24 @@ config ACPI_SYSFS_POWER | |||
104 | help | 104 | help |
105 | Say N to disable power /sys interface | 105 | Say N to disable power /sys interface |
106 | 106 | ||
107 | config ACPI_EC_DEBUGFS | ||
108 | tristate "EC read/write access through /sys/kernel/debug/ec" | ||
109 | default n | ||
110 | help | ||
111 | Say N to disable Embedded Controller /sys/kernel/debug interface | ||
112 | |||
113 | Be aware that using this interface can confuse your Embedded | ||
114 | Controller in a way that a normal reboot is not enough. You then | ||
115 | have to power of your system, and remove the laptop battery for | ||
116 | some seconds. | ||
117 | An Embedded Controller typically is available on laptops and reads | ||
118 | sensor values like battery state and temperature. | ||
119 | The kernel accesses the EC through ACPI parsed code provided by BIOS | ||
120 | tables. This option allows to access the EC directly without ACPI | ||
121 | code being involved. | ||
122 | Thus this option is a debug option that helps to write ACPI drivers | ||
123 | and can be used to identify ACPI code or EC firmware bugs. | ||
124 | |||
107 | config ACPI_PROC_EVENT | 125 | config ACPI_PROC_EVENT |
108 | bool "Deprecated /proc/acpi/event support" | 126 | bool "Deprecated /proc/acpi/event support" |
109 | depends on PROC_FS | 127 | depends on PROC_FS |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 6ee33169e1dc..833b582d1762 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -60,6 +60,7 @@ obj-$(CONFIG_ACPI_SBS) += sbshc.o | |||
60 | obj-$(CONFIG_ACPI_SBS) += sbs.o | 60 | obj-$(CONFIG_ACPI_SBS) += sbs.o |
61 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o | 61 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o |
62 | obj-$(CONFIG_ACPI_HED) += hed.o | 62 | obj-$(CONFIG_ACPI_HED) += hed.o |
63 | obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o | ||
63 | 64 | ||
64 | # processor has its own "processor." module_param namespace | 65 | # processor has its own "processor." module_param namespace |
65 | processor-y := processor_driver.o processor_throttling.o | 66 | processor-y := processor_driver.o processor_throttling.o |
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 5f2027d782e8..1fa0aafebe2a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -34,8 +34,6 @@ | |||
34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
35 | #include <linux/types.h> | 35 | #include <linux/types.h> |
36 | #include <linux/delay.h> | 36 | #include <linux/delay.h> |
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/seq_file.h> | ||
39 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
40 | #include <linux/list.h> | 38 | #include <linux/list.h> |
41 | #include <linux/spinlock.h> | 39 | #include <linux/spinlock.h> |
@@ -45,10 +43,13 @@ | |||
45 | #include <acpi/acpi_drivers.h> | 43 | #include <acpi/acpi_drivers.h> |
46 | #include <linux/dmi.h> | 44 | #include <linux/dmi.h> |
47 | 45 | ||
46 | #include "internal.h" | ||
47 | |||
48 | #define ACPI_EC_CLASS "embedded_controller" | 48 | #define ACPI_EC_CLASS "embedded_controller" |
49 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" | 49 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" |
50 | #define ACPI_EC_FILE_INFO "info" | 50 | #define ACPI_EC_FILE_INFO "info" |
51 | 51 | ||
52 | #undef PREFIX | ||
52 | #define PREFIX "ACPI: EC: " | 53 | #define PREFIX "ACPI: EC: " |
53 | 54 | ||
54 | /* EC status register */ | 55 | /* EC status register */ |
@@ -106,19 +107,8 @@ struct transaction { | |||
106 | bool done; | 107 | bool done; |
107 | }; | 108 | }; |
108 | 109 | ||
109 | static struct acpi_ec { | 110 | struct acpi_ec *boot_ec, *first_ec; |
110 | acpi_handle handle; | 111 | EXPORT_SYMBOL(first_ec); |
111 | unsigned long gpe; | ||
112 | unsigned long command_addr; | ||
113 | unsigned long data_addr; | ||
114 | unsigned long global_lock; | ||
115 | unsigned long flags; | ||
116 | struct mutex lock; | ||
117 | wait_queue_head_t wait; | ||
118 | struct list_head list; | ||
119 | struct transaction *curr; | ||
120 | spinlock_t curr_lock; | ||
121 | } *boot_ec, *first_ec; | ||
122 | 112 | ||
123 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ | 113 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ |
124 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ | 114 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ |
@@ -679,72 +669,6 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, | |||
679 | } | 669 | } |
680 | 670 | ||
681 | /* -------------------------------------------------------------------------- | 671 | /* -------------------------------------------------------------------------- |
682 | FS Interface (/proc) | ||
683 | -------------------------------------------------------------------------- */ | ||
684 | |||
685 | static struct proc_dir_entry *acpi_ec_dir; | ||
686 | |||
687 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) | ||
688 | { | ||
689 | struct acpi_ec *ec = seq->private; | ||
690 | |||
691 | if (!ec) | ||
692 | goto end; | ||
693 | |||
694 | seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); | ||
695 | seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", | ||
696 | (unsigned)ec->command_addr, (unsigned)ec->data_addr); | ||
697 | seq_printf(seq, "use global lock:\t%s\n", | ||
698 | ec->global_lock ? "yes" : "no"); | ||
699 | end: | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) | ||
704 | { | ||
705 | return single_open(file, acpi_ec_read_info, PDE(inode)->data); | ||
706 | } | ||
707 | |||
708 | static const struct file_operations acpi_ec_info_ops = { | ||
709 | .open = acpi_ec_info_open_fs, | ||
710 | .read = seq_read, | ||
711 | .llseek = seq_lseek, | ||
712 | .release = single_release, | ||
713 | .owner = THIS_MODULE, | ||
714 | }; | ||
715 | |||
716 | static int acpi_ec_add_fs(struct acpi_device *device) | ||
717 | { | ||
718 | struct proc_dir_entry *entry = NULL; | ||
719 | |||
720 | if (!acpi_device_dir(device)) { | ||
721 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
722 | acpi_ec_dir); | ||
723 | if (!acpi_device_dir(device)) | ||
724 | return -ENODEV; | ||
725 | } | ||
726 | |||
727 | entry = proc_create_data(ACPI_EC_FILE_INFO, S_IRUGO, | ||
728 | acpi_device_dir(device), | ||
729 | &acpi_ec_info_ops, acpi_driver_data(device)); | ||
730 | if (!entry) | ||
731 | return -ENODEV; | ||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int acpi_ec_remove_fs(struct acpi_device *device) | ||
736 | { | ||
737 | |||
738 | if (acpi_device_dir(device)) { | ||
739 | remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device)); | ||
740 | remove_proc_entry(acpi_device_bid(device), acpi_ec_dir); | ||
741 | acpi_device_dir(device) = NULL; | ||
742 | } | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | /* -------------------------------------------------------------------------- | ||
748 | Driver Interface | 672 | Driver Interface |
749 | -------------------------------------------------------------------------- */ | 673 | -------------------------------------------------------------------------- */ |
750 | static acpi_status | 674 | static acpi_status |
@@ -894,7 +818,12 @@ static int acpi_ec_add(struct acpi_device *device) | |||
894 | if (!first_ec) | 818 | if (!first_ec) |
895 | first_ec = ec; | 819 | first_ec = ec; |
896 | device->driver_data = ec; | 820 | device->driver_data = ec; |
897 | acpi_ec_add_fs(device); | 821 | |
822 | WARN(!request_region(ec->data_addr, 1, "EC data"), | ||
823 | "Could not request EC data io port 0x%lx", ec->data_addr); | ||
824 | WARN(!request_region(ec->command_addr, 1, "EC cmd"), | ||
825 | "Could not request EC cmd io port 0x%lx", ec->command_addr); | ||
826 | |||
898 | pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", | 827 | pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", |
899 | ec->gpe, ec->command_addr, ec->data_addr); | 828 | ec->gpe, ec->command_addr, ec->data_addr); |
900 | 829 | ||
@@ -921,7 +850,8 @@ static int acpi_ec_remove(struct acpi_device *device, int type) | |||
921 | kfree(handler); | 850 | kfree(handler); |
922 | } | 851 | } |
923 | mutex_unlock(&ec->lock); | 852 | mutex_unlock(&ec->lock); |
924 | acpi_ec_remove_fs(device); | 853 | release_region(ec->data_addr, 1); |
854 | release_region(ec->command_addr, 1); | ||
925 | device->driver_data = NULL; | 855 | device->driver_data = NULL; |
926 | if (ec == first_ec) | 856 | if (ec == first_ec) |
927 | first_ec = NULL; | 857 | first_ec = NULL; |
@@ -1120,16 +1050,10 @@ int __init acpi_ec_init(void) | |||
1120 | { | 1050 | { |
1121 | int result = 0; | 1051 | int result = 0; |
1122 | 1052 | ||
1123 | acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir); | ||
1124 | if (!acpi_ec_dir) | ||
1125 | return -ENODEV; | ||
1126 | |||
1127 | /* Now register the driver for the EC */ | 1053 | /* Now register the driver for the EC */ |
1128 | result = acpi_bus_register_driver(&acpi_ec_driver); | 1054 | result = acpi_bus_register_driver(&acpi_ec_driver); |
1129 | if (result < 0) { | 1055 | if (result < 0) |
1130 | remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); | ||
1131 | return -ENODEV; | 1056 | return -ENODEV; |
1132 | } | ||
1133 | 1057 | ||
1134 | return result; | 1058 | return result; |
1135 | } | 1059 | } |
@@ -1140,9 +1064,6 @@ static void __exit acpi_ec_exit(void) | |||
1140 | { | 1064 | { |
1141 | 1065 | ||
1142 | acpi_bus_unregister_driver(&acpi_ec_driver); | 1066 | acpi_bus_unregister_driver(&acpi_ec_driver); |
1143 | |||
1144 | remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); | ||
1145 | |||
1146 | return; | 1067 | return; |
1147 | } | 1068 | } |
1148 | #endif /* 0 */ | 1069 | #endif /* 0 */ |
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c new file mode 100644 index 000000000000..0e869b3f81ca --- /dev/null +++ b/drivers/acpi/ec_sys.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * ec_sys.c | ||
3 | * | ||
4 | * Copyright (C) 2010 SUSE Products GmbH/Novell | ||
5 | * Author: | ||
6 | * Thomas Renninger <trenn@suse.de> | ||
7 | * | ||
8 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/acpi.h> | ||
13 | #include <linux/debugfs.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); | ||
17 | MODULE_DESCRIPTION("ACPI EC sysfs access driver"); | ||
18 | MODULE_LICENSE("GPL"); | ||
19 | |||
20 | static bool write_support; | ||
21 | module_param(write_support, bool, 0644); | ||
22 | MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " | ||
23 | "be needed."); | ||
24 | |||
25 | #define EC_SPACE_SIZE 256 | ||
26 | |||
27 | struct sysdev_class acpi_ec_sysdev_class = { | ||
28 | .name = "ec", | ||
29 | }; | ||
30 | |||
31 | static struct dentry *acpi_ec_debugfs_dir; | ||
32 | |||
33 | static int acpi_ec_open_io(struct inode *i, struct file *f) | ||
34 | { | ||
35 | f->private_data = i->i_private; | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, | ||
40 | size_t count, loff_t *off) | ||
41 | { | ||
42 | /* Use this if support reading/writing multiple ECs exists in ec.c: | ||
43 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | ||
44 | */ | ||
45 | unsigned int size = EC_SPACE_SIZE; | ||
46 | u8 *data = (u8 *) buf; | ||
47 | loff_t init_off = *off; | ||
48 | int err = 0; | ||
49 | |||
50 | if (*off >= size) | ||
51 | return 0; | ||
52 | if (*off + count >= size) { | ||
53 | size -= *off; | ||
54 | count = size; | ||
55 | } else | ||
56 | size = count; | ||
57 | |||
58 | while (size) { | ||
59 | err = ec_read(*off, &data[*off - init_off]); | ||
60 | if (err) | ||
61 | return err; | ||
62 | *off += 1; | ||
63 | size--; | ||
64 | } | ||
65 | return count; | ||
66 | } | ||
67 | |||
68 | static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, | ||
69 | size_t count, loff_t *off) | ||
70 | { | ||
71 | /* Use this if support reading/writing multiple ECs exists in ec.c: | ||
72 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | ||
73 | */ | ||
74 | |||
75 | unsigned int size = count; | ||
76 | loff_t init_off = *off; | ||
77 | u8 *data = (u8 *) buf; | ||
78 | int err = 0; | ||
79 | |||
80 | if (*off >= EC_SPACE_SIZE) | ||
81 | return 0; | ||
82 | if (*off + count >= EC_SPACE_SIZE) { | ||
83 | size = EC_SPACE_SIZE - *off; | ||
84 | count = size; | ||
85 | } | ||
86 | |||
87 | while (size) { | ||
88 | u8 byte_write = data[*off - init_off]; | ||
89 | err = ec_write(*off, byte_write); | ||
90 | if (err) | ||
91 | return err; | ||
92 | |||
93 | *off += 1; | ||
94 | size--; | ||
95 | } | ||
96 | return count; | ||
97 | } | ||
98 | |||
99 | static struct file_operations acpi_ec_io_ops = { | ||
100 | .owner = THIS_MODULE, | ||
101 | .open = acpi_ec_open_io, | ||
102 | .read = acpi_ec_read_io, | ||
103 | .write = acpi_ec_write_io, | ||
104 | }; | ||
105 | |||
106 | int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) | ||
107 | { | ||
108 | struct dentry *dev_dir; | ||
109 | char name[64]; | ||
110 | mode_t mode = 0400; | ||
111 | |||
112 | if (ec_device_count == 0) { | ||
113 | acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); | ||
114 | if (!acpi_ec_debugfs_dir) | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | |||
118 | sprintf(name, "ec%u", ec_device_count); | ||
119 | dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir); | ||
120 | if (!dev_dir) { | ||
121 | if (ec_device_count != 0) | ||
122 | goto error; | ||
123 | return -ENOMEM; | ||
124 | } | ||
125 | |||
126 | if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe)) | ||
127 | goto error; | ||
128 | if (!debugfs_create_bool("use_global_lock", 0444, dev_dir, | ||
129 | (u32 *)&first_ec->global_lock)) | ||
130 | goto error; | ||
131 | |||
132 | if (write_support) | ||
133 | mode = 0600; | ||
134 | if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops)) | ||
135 | goto error; | ||
136 | |||
137 | return 0; | ||
138 | |||
139 | error: | ||
140 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | static int __init acpi_ec_sys_init(void) | ||
145 | { | ||
146 | int err = 0; | ||
147 | if (first_ec) | ||
148 | err = acpi_ec_add_debugfs(first_ec, 0); | ||
149 | else | ||
150 | err = -ENODEV; | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | static void __exit acpi_ec_sys_exit(void) | ||
155 | { | ||
156 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | ||
157 | } | ||
158 | |||
159 | module_init(acpi_ec_sys_init); | ||
160 | module_exit(acpi_ec_sys_exit); | ||
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index f8f190ec066e..8ae27264a00e 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -18,6 +18,11 @@ | |||
18 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #ifndef _ACPI_INTERNAL_H_ | ||
22 | #define _ACPI_INTERNAL_H_ | ||
23 | |||
24 | #include <linux/sysdev.h> | ||
25 | |||
21 | #define PREFIX "ACPI: " | 26 | #define PREFIX "ACPI: " |
22 | 27 | ||
23 | int init_acpi_device_notify(void); | 28 | int init_acpi_device_notify(void); |
@@ -46,6 +51,23 @@ void acpi_early_processor_set_pdc(void); | |||
46 | /* -------------------------------------------------------------------------- | 51 | /* -------------------------------------------------------------------------- |
47 | Embedded Controller | 52 | Embedded Controller |
48 | -------------------------------------------------------------------------- */ | 53 | -------------------------------------------------------------------------- */ |
54 | struct acpi_ec { | ||
55 | acpi_handle handle; | ||
56 | unsigned long gpe; | ||
57 | unsigned long command_addr; | ||
58 | unsigned long data_addr; | ||
59 | unsigned long global_lock; | ||
60 | unsigned long flags; | ||
61 | struct mutex lock; | ||
62 | wait_queue_head_t wait; | ||
63 | struct list_head list; | ||
64 | struct transaction *curr; | ||
65 | spinlock_t curr_lock; | ||
66 | struct sys_device sysdev; | ||
67 | }; | ||
68 | |||
69 | extern struct acpi_ec *first_ec; | ||
70 | |||
49 | int acpi_ec_init(void); | 71 | int acpi_ec_init(void); |
50 | int acpi_ec_ecdt_probe(void); | 72 | int acpi_ec_ecdt_probe(void); |
51 | int acpi_boot_ec_enable(void); | 73 | int acpi_boot_ec_enable(void); |
@@ -63,3 +85,5 @@ int acpi_sleep_proc_init(void); | |||
63 | #else | 85 | #else |
64 | static inline int acpi_sleep_proc_init(void) { return 0; } | 86 | static inline int acpi_sleep_proc_init(void) { return 0; } |
65 | #endif | 87 | #endif |
88 | |||
89 | #endif /* _ACPI_INTERNAL_H_ */ | ||