diff options
-rw-r--r-- | drivers/acpi/Kconfig | 12 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/ac.c | 12 | ||||
-rw-r--r-- | drivers/acpi/battery.c | 13 | ||||
-rw-r--r-- | drivers/acpi/cm_sbs.c | 135 | ||||
-rw-r--r-- | drivers/acpi/i2c_ec.c | 420 | ||||
-rw-r--r-- | drivers/acpi/i2c_ec.h | 23 | ||||
-rw-r--r-- | drivers/acpi/sbs.c | 1828 |
8 files changed, 2434 insertions, 11 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index bc2652d72fdc..fef7bab12244 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -352,6 +352,18 @@ config ACPI_HOTPLUG_MEMORY | |||
352 | If one selects "m," this driver can be loaded using the following | 352 | If one selects "m," this driver can be loaded using the following |
353 | command: | 353 | command: |
354 | $>modprobe acpi_memhotplug | 354 | $>modprobe acpi_memhotplug |
355 | |||
356 | config ACPI_SBS | ||
357 | tristate "Smart Battery System (EXPERIMENTAL)" | ||
358 | depends on X86 && I2C | ||
359 | depends on EXPERIMENTAL | ||
360 | default y | ||
361 | help | ||
362 | This driver adds support for the Smart Battery System. | ||
363 | Depends on I2C (Device Drivers ---> I2C support) | ||
364 | A "Smart Battery" is quite old and quite rare compared | ||
365 | to today's ACPI "Control Method" battery. | ||
366 | |||
355 | endif # ACPI | 367 | endif # ACPI |
356 | 368 | ||
357 | endmenu | 369 | endmenu |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index f0a68ecf1e57..bce7ca27b429 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -58,3 +58,5 @@ obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o | |||
58 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 58 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
59 | obj-y += scan.o motherboard.o | 59 | obj-y += scan.o motherboard.o |
60 | obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o | 60 | obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o |
61 | obj-y += cm_sbs.o | ||
62 | obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o | ||
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 4537ae4838c4..e0a1b1541362 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c | |||
@@ -50,6 +50,9 @@ ACPI_MODULE_NAME("acpi_ac") | |||
50 | MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME); | 50 | MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME); |
51 | MODULE_LICENSE("GPL"); | 51 | MODULE_LICENSE("GPL"); |
52 | 52 | ||
53 | extern struct proc_dir_entry *acpi_lock_ac_dir(void); | ||
54 | extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); | ||
55 | |||
53 | static int acpi_ac_add(struct acpi_device *device); | 56 | static int acpi_ac_add(struct acpi_device *device); |
54 | static int acpi_ac_remove(struct acpi_device *device, int type); | 57 | static int acpi_ac_remove(struct acpi_device *device, int type); |
55 | static int acpi_ac_open_fs(struct inode *inode, struct file *file); | 58 | static int acpi_ac_open_fs(struct inode *inode, struct file *file); |
@@ -278,17 +281,16 @@ static int acpi_ac_remove(struct acpi_device *device, int type) | |||
278 | 281 | ||
279 | static int __init acpi_ac_init(void) | 282 | static int __init acpi_ac_init(void) |
280 | { | 283 | { |
281 | int result = 0; | 284 | int result; |
282 | 285 | ||
283 | 286 | ||
284 | acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); | 287 | acpi_ac_dir = acpi_lock_ac_dir(); |
285 | if (!acpi_ac_dir) | 288 | if (!acpi_ac_dir) |
286 | return -ENODEV; | 289 | return -ENODEV; |
287 | acpi_ac_dir->owner = THIS_MODULE; | ||
288 | 290 | ||
289 | result = acpi_bus_register_driver(&acpi_ac_driver); | 291 | result = acpi_bus_register_driver(&acpi_ac_driver); |
290 | if (result < 0) { | 292 | if (result < 0) { |
291 | remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); | 293 | acpi_unlock_ac_dir(acpi_ac_dir); |
292 | return -ENODEV; | 294 | return -ENODEV; |
293 | } | 295 | } |
294 | 296 | ||
@@ -300,7 +302,7 @@ static void __exit acpi_ac_exit(void) | |||
300 | 302 | ||
301 | acpi_bus_unregister_driver(&acpi_ac_driver); | 303 | acpi_bus_unregister_driver(&acpi_ac_driver); |
302 | 304 | ||
303 | remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); | 305 | acpi_unlock_ac_dir(acpi_ac_dir); |
304 | 306 | ||
305 | return; | 307 | return; |
306 | } | 308 | } |
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 2b8aab560b58..3ea79decfe24 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c | |||
@@ -59,6 +59,9 @@ ACPI_MODULE_NAME("acpi_battery") | |||
59 | MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME); | 59 | MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME); |
60 | MODULE_LICENSE("GPL"); | 60 | MODULE_LICENSE("GPL"); |
61 | 61 | ||
62 | extern struct proc_dir_entry *acpi_lock_battery_dir(void); | ||
63 | extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); | ||
64 | |||
62 | static int acpi_battery_add(struct acpi_device *device); | 65 | static int acpi_battery_add(struct acpi_device *device); |
63 | static int acpi_battery_remove(struct acpi_device *device, int type); | 66 | static int acpi_battery_remove(struct acpi_device *device, int type); |
64 | 67 | ||
@@ -750,17 +753,15 @@ static int acpi_battery_remove(struct acpi_device *device, int type) | |||
750 | 753 | ||
751 | static int __init acpi_battery_init(void) | 754 | static int __init acpi_battery_init(void) |
752 | { | 755 | { |
753 | int result = 0; | 756 | int result; |
754 | |||
755 | 757 | ||
756 | acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); | 758 | acpi_battery_dir = acpi_lock_battery_dir(); |
757 | if (!acpi_battery_dir) | 759 | if (!acpi_battery_dir) |
758 | return -ENODEV; | 760 | return -ENODEV; |
759 | acpi_battery_dir->owner = THIS_MODULE; | ||
760 | 761 | ||
761 | result = acpi_bus_register_driver(&acpi_battery_driver); | 762 | result = acpi_bus_register_driver(&acpi_battery_driver); |
762 | if (result < 0) { | 763 | if (result < 0) { |
763 | remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); | 764 | acpi_unlock_battery_dir(acpi_battery_dir); |
764 | return -ENODEV; | 765 | return -ENODEV; |
765 | } | 766 | } |
766 | 767 | ||
@@ -772,7 +773,7 @@ static void __exit acpi_battery_exit(void) | |||
772 | 773 | ||
773 | acpi_bus_unregister_driver(&acpi_battery_driver); | 774 | acpi_bus_unregister_driver(&acpi_battery_driver); |
774 | 775 | ||
775 | remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); | 776 | acpi_unlock_battery_dir(acpi_battery_dir); |
776 | 777 | ||
777 | return; | 778 | return; |
778 | } | 779 | } |
diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c new file mode 100644 index 000000000000..d11507c7b8a4 --- /dev/null +++ b/drivers/acpi/cm_sbs.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or (at | ||
7 | * your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | * General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
17 | * | ||
18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/acpi.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/proc_fs.h> | ||
27 | #include <linux/seq_file.h> | ||
28 | #include <acpi/acpi_bus.h> | ||
29 | #include <acpi/acpi_drivers.h> | ||
30 | #include <acpi/acmacros.h> | ||
31 | #include <acpi/actypes.h> | ||
32 | #include <acpi/acutils.h> | ||
33 | |||
34 | ACPI_MODULE_NAME("cm_sbs") | ||
35 | #define ACPI_AC_CLASS "ac_adapter" | ||
36 | #define ACPI_BATTERY_CLASS "battery" | ||
37 | #define ACPI_SBS_COMPONENT 0x00080000 | ||
38 | #define _COMPONENT ACPI_SBS_COMPONENT | ||
39 | static struct proc_dir_entry *acpi_ac_dir; | ||
40 | static struct proc_dir_entry *acpi_battery_dir; | ||
41 | |||
42 | static struct semaphore cm_sbs_sem; | ||
43 | |||
44 | static int lock_ac_dir_cnt = 0; | ||
45 | static int lock_battery_dir_cnt = 0; | ||
46 | |||
47 | struct proc_dir_entry *acpi_lock_ac_dir(void) | ||
48 | { | ||
49 | ACPI_FUNCTION_TRACE("acpi_lock_ac_dir"); | ||
50 | |||
51 | down(&cm_sbs_sem); | ||
52 | if (!acpi_ac_dir) { | ||
53 | acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); | ||
54 | } | ||
55 | if (acpi_ac_dir) { | ||
56 | lock_ac_dir_cnt++; | ||
57 | } else { | ||
58 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
59 | "Cannot create %s\n", ACPI_AC_CLASS)); | ||
60 | } | ||
61 | up(&cm_sbs_sem); | ||
62 | return (acpi_ac_dir); | ||
63 | } | ||
64 | |||
65 | EXPORT_SYMBOL(acpi_lock_ac_dir); | ||
66 | |||
67 | void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param) | ||
68 | { | ||
69 | ACPI_FUNCTION_TRACE("acpi_unlock_ac_dir"); | ||
70 | |||
71 | down(&cm_sbs_sem); | ||
72 | if (acpi_ac_dir_param) { | ||
73 | lock_ac_dir_cnt--; | ||
74 | } | ||
75 | if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) { | ||
76 | remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); | ||
77 | acpi_ac_dir = 0; | ||
78 | } | ||
79 | up(&cm_sbs_sem); | ||
80 | } | ||
81 | |||
82 | EXPORT_SYMBOL(acpi_unlock_ac_dir); | ||
83 | |||
84 | struct proc_dir_entry *acpi_lock_battery_dir(void) | ||
85 | { | ||
86 | ACPI_FUNCTION_TRACE("acpi_lock_battery_dir"); | ||
87 | |||
88 | down(&cm_sbs_sem); | ||
89 | if (!acpi_battery_dir) { | ||
90 | acpi_battery_dir = | ||
91 | proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); | ||
92 | } | ||
93 | if (acpi_battery_dir) { | ||
94 | lock_battery_dir_cnt++; | ||
95 | } else { | ||
96 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
97 | "Cannot create %s\n", ACPI_BATTERY_CLASS)); | ||
98 | } | ||
99 | up(&cm_sbs_sem); | ||
100 | return (acpi_battery_dir); | ||
101 | } | ||
102 | |||
103 | EXPORT_SYMBOL(acpi_lock_battery_dir); | ||
104 | |||
105 | void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) | ||
106 | { | ||
107 | ACPI_FUNCTION_TRACE("acpi_unlock_battery_dir"); | ||
108 | |||
109 | down(&cm_sbs_sem); | ||
110 | if (acpi_battery_dir_param) { | ||
111 | lock_battery_dir_cnt--; | ||
112 | } | ||
113 | if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param | ||
114 | && acpi_battery_dir) { | ||
115 | remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); | ||
116 | acpi_battery_dir = 0; | ||
117 | } | ||
118 | up(&cm_sbs_sem); | ||
119 | } | ||
120 | |||
121 | EXPORT_SYMBOL(acpi_unlock_battery_dir); | ||
122 | |||
123 | static int __init acpi_cm_sbs_init(void) | ||
124 | { | ||
125 | ACPI_FUNCTION_TRACE("acpi_cm_sbs_init"); | ||
126 | |||
127 | if (acpi_disabled) | ||
128 | return_VALUE(0); | ||
129 | |||
130 | init_MUTEX(&cm_sbs_sem); | ||
131 | |||
132 | return_VALUE(0); | ||
133 | } | ||
134 | |||
135 | subsys_initcall(acpi_cm_sbs_init); | ||
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c new file mode 100644 index 000000000000..72478a665c8c --- /dev/null +++ b/drivers/acpi/i2c_ec.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | * SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $) | ||
3 | * | ||
4 | * Copyright (c) 2002, 2005 Ducrot Bruno | ||
5 | * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation version 2. | ||
10 | */ | ||
11 | |||
12 | #include <linux/version.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/stddef.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/i2c.h> | ||
20 | #include <linux/acpi.h> | ||
21 | #include <linux/delay.h> | ||
22 | |||
23 | #include "i2c_ec.h" | ||
24 | |||
25 | #define xudelay(t) udelay(t) | ||
26 | #define xmsleep(t) msleep(t) | ||
27 | |||
28 | #define ACPI_EC_HC_COMPONENT 0x00080000 | ||
29 | #define ACPI_EC_HC_CLASS "ec_hc_smbus" | ||
30 | #define ACPI_EC_HC_HID "ACPI0001" | ||
31 | #define ACPI_EC_HC_DRIVER_NAME "ACPI EC HC smbus driver" | ||
32 | #define ACPI_EC_HC_DEVICE_NAME "EC HC smbus" | ||
33 | |||
34 | #define _COMPONENT ACPI_EC_HC_COMPONENT | ||
35 | |||
36 | ACPI_MODULE_NAME("acpi_smbus") | ||
37 | |||
38 | static int acpi_ec_hc_add(struct acpi_device *device); | ||
39 | static int acpi_ec_hc_remove(struct acpi_device *device, int type); | ||
40 | |||
41 | static struct acpi_driver acpi_ec_hc_driver = { | ||
42 | .name = ACPI_EC_HC_DRIVER_NAME, | ||
43 | .class = ACPI_EC_HC_CLASS, | ||
44 | .ids = ACPI_EC_HC_HID, | ||
45 | .ops = { | ||
46 | .add = acpi_ec_hc_add, | ||
47 | .remove = acpi_ec_hc_remove, | ||
48 | }, | ||
49 | }; | ||
50 | |||
51 | /* Various bit mask for EC_SC (R) */ | ||
52 | #define OBF 0x01 | ||
53 | #define IBF 0x02 | ||
54 | #define CMD 0x08 | ||
55 | #define BURST 0x10 | ||
56 | #define SCI_EVT 0x20 | ||
57 | #define SMI_EVT 0x40 | ||
58 | |||
59 | /* Commands for EC_SC (W) */ | ||
60 | #define RD_EC 0x80 | ||
61 | #define WR_EC 0x81 | ||
62 | #define BE_EC 0x82 | ||
63 | #define BD_EC 0x83 | ||
64 | #define QR_EC 0x84 | ||
65 | |||
66 | /* | ||
67 | * ACPI 2.0 chapter 13 SMBus 2.0 EC register model | ||
68 | */ | ||
69 | |||
70 | #define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ | ||
71 | #define ACPI_EC_SMB_STS 0x01 /* status */ | ||
72 | #define ACPI_EC_SMB_ADDR 0x02 /* address */ | ||
73 | #define ACPI_EC_SMB_CMD 0x03 /* command */ | ||
74 | #define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ | ||
75 | #define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ | ||
76 | #define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */ | ||
77 | #define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ | ||
78 | |||
79 | #define ACPI_EC_SMB_STS_DONE 0x80 | ||
80 | #define ACPI_EC_SMB_STS_ALRM 0x40 | ||
81 | #define ACPI_EC_SMB_STS_RES 0x20 | ||
82 | #define ACPI_EC_SMB_STS_STATUS 0x1f | ||
83 | |||
84 | #define ACPI_EC_SMB_STATUS_OK 0x00 | ||
85 | #define ACPI_EC_SMB_STATUS_FAIL 0x07 | ||
86 | #define ACPI_EC_SMB_STATUS_DNAK 0x10 | ||
87 | #define ACPI_EC_SMB_STATUS_DERR 0x11 | ||
88 | #define ACPI_EC_SMB_STATUS_CMD_DENY 0x12 | ||
89 | #define ACPI_EC_SMB_STATUS_UNKNOWN 0x13 | ||
90 | #define ACPI_EC_SMB_STATUS_ACC_DENY 0x17 | ||
91 | #define ACPI_EC_SMB_STATUS_TIMEOUT 0x18 | ||
92 | #define ACPI_EC_SMB_STATUS_NOTSUP 0x19 | ||
93 | #define ACPI_EC_SMB_STATUS_BUSY 0x1A | ||
94 | #define ACPI_EC_SMB_STATUS_PEC 0x1F | ||
95 | |||
96 | #define ACPI_EC_SMB_PRTCL_WRITE 0x00 | ||
97 | #define ACPI_EC_SMB_PRTCL_READ 0x01 | ||
98 | #define ACPI_EC_SMB_PRTCL_QUICK 0x02 | ||
99 | #define ACPI_EC_SMB_PRTCL_BYTE 0x04 | ||
100 | #define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06 | ||
101 | #define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 | ||
102 | #define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a | ||
103 | #define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c | ||
104 | #define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d | ||
105 | #define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a | ||
106 | #define ACPI_EC_SMB_PRTCL_PEC 0x80 | ||
107 | |||
108 | /* Length of pre/post transaction sleep (msec) */ | ||
109 | #define ACPI_EC_SMB_TRANSACTION_SLEEP 1 | ||
110 | #define ACPI_EC_SMB_ACCESS_SLEEP1 1 | ||
111 | #define ACPI_EC_SMB_ACCESS_SLEEP2 10 | ||
112 | |||
113 | static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data) | ||
114 | { | ||
115 | u8 val; | ||
116 | int err; | ||
117 | |||
118 | ACPI_FUNCTION_TRACE("acpi_ec_smb_read"); | ||
119 | |||
120 | err = ec_read(smbus->base + address, &val); | ||
121 | if (!err) { | ||
122 | *data = val; | ||
123 | } | ||
124 | xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); | ||
125 | return (err); | ||
126 | } | ||
127 | |||
128 | static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data) | ||
129 | { | ||
130 | int err; | ||
131 | |||
132 | ACPI_FUNCTION_TRACE("acpi_ec_smb_write"); | ||
133 | |||
134 | err = ec_write(smbus->base + address, data); | ||
135 | return (err); | ||
136 | } | ||
137 | |||
138 | static int | ||
139 | acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, | ||
140 | char read_write, u8 command, int size, | ||
141 | union i2c_smbus_data *data) | ||
142 | { | ||
143 | struct acpi_ec_smbus *smbus = adap->algo_data; | ||
144 | unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 }; | ||
145 | int i; | ||
146 | |||
147 | ACPI_FUNCTION_TRACE("acpi_ec_smb_access"); | ||
148 | |||
149 | if (read_write == I2C_SMBUS_READ) { | ||
150 | protocol = ACPI_EC_SMB_PRTCL_READ; | ||
151 | } else { | ||
152 | protocol = ACPI_EC_SMB_PRTCL_WRITE; | ||
153 | } | ||
154 | pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0; | ||
155 | |||
156 | switch (size) { | ||
157 | |||
158 | case I2C_SMBUS_QUICK: | ||
159 | protocol |= ACPI_EC_SMB_PRTCL_QUICK; | ||
160 | read_write = I2C_SMBUS_WRITE; | ||
161 | break; | ||
162 | |||
163 | case I2C_SMBUS_BYTE: | ||
164 | if (read_write == I2C_SMBUS_WRITE) { | ||
165 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); | ||
166 | } | ||
167 | protocol |= ACPI_EC_SMB_PRTCL_BYTE; | ||
168 | break; | ||
169 | |||
170 | case I2C_SMBUS_BYTE_DATA: | ||
171 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
172 | if (read_write == I2C_SMBUS_WRITE) { | ||
173 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); | ||
174 | } | ||
175 | protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA; | ||
176 | break; | ||
177 | |||
178 | case I2C_SMBUS_WORD_DATA: | ||
179 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
180 | if (read_write == I2C_SMBUS_WRITE) { | ||
181 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); | ||
182 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, | ||
183 | data->word >> 8); | ||
184 | } | ||
185 | protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec; | ||
186 | break; | ||
187 | |||
188 | case I2C_SMBUS_BLOCK_DATA: | ||
189 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
190 | if (read_write == I2C_SMBUS_WRITE) { | ||
191 | len = min_t(u8, data->block[0], 32); | ||
192 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); | ||
193 | for (i = 0; i < len; i++) | ||
194 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, | ||
195 | data->block[i + 1]); | ||
196 | } | ||
197 | protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec; | ||
198 | break; | ||
199 | |||
200 | case I2C_SMBUS_I2C_BLOCK_DATA: | ||
201 | len = min_t(u8, data->block[0], 32); | ||
202 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
203 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); | ||
204 | if (read_write == I2C_SMBUS_WRITE) { | ||
205 | for (i = 0; i < len; i++) { | ||
206 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, | ||
207 | data->block[i + 1]); | ||
208 | } | ||
209 | } | ||
210 | protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA; | ||
211 | break; | ||
212 | |||
213 | case I2C_SMBUS_PROC_CALL: | ||
214 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
215 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); | ||
216 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8); | ||
217 | protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec; | ||
218 | read_write = I2C_SMBUS_READ; | ||
219 | break; | ||
220 | |||
221 | case I2C_SMBUS_BLOCK_PROC_CALL: | ||
222 | protocol |= pec; | ||
223 | len = min_t(u8, data->block[0], 31); | ||
224 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); | ||
225 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); | ||
226 | for (i = 0; i < len; i++) | ||
227 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, | ||
228 | data->block[i + 1]); | ||
229 | protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec; | ||
230 | read_write = I2C_SMBUS_READ; | ||
231 | break; | ||
232 | |||
233 | default: | ||
234 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: " | ||
235 | "Unsupported transaction %d\n", size)); | ||
236 | return (-1); | ||
237 | } | ||
238 | |||
239 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1); | ||
240 | acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol); | ||
241 | |||
242 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); | ||
243 | |||
244 | if (~temp[0] & ACPI_EC_SMB_STS_DONE) { | ||
245 | xudelay(500); | ||
246 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); | ||
247 | } | ||
248 | if (~temp[0] & ACPI_EC_SMB_STS_DONE) { | ||
249 | xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); | ||
250 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); | ||
251 | } | ||
252 | if ((~temp[0] & ACPI_EC_SMB_STS_DONE) | ||
253 | || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { | ||
254 | return (-1); | ||
255 | } | ||
256 | |||
257 | if (read_write == I2C_SMBUS_WRITE) { | ||
258 | return (0); | ||
259 | } | ||
260 | |||
261 | switch (size) { | ||
262 | |||
263 | case I2C_SMBUS_BYTE: | ||
264 | case I2C_SMBUS_BYTE_DATA: | ||
265 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte); | ||
266 | break; | ||
267 | |||
268 | case I2C_SMBUS_WORD_DATA: | ||
269 | case I2C_SMBUS_PROC_CALL: | ||
270 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0); | ||
271 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1); | ||
272 | data->word = (temp[1] << 8) | temp[0]; | ||
273 | break; | ||
274 | |||
275 | case I2C_SMBUS_BLOCK_DATA: | ||
276 | case I2C_SMBUS_BLOCK_PROC_CALL: | ||
277 | len = 0; | ||
278 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len); | ||
279 | len = min_t(u8, len, 32); | ||
280 | case I2C_SMBUS_I2C_BLOCK_DATA: | ||
281 | for (i = 0; i < len; i++) | ||
282 | acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i, | ||
283 | data->block + i + 1); | ||
284 | data->block[0] = len; | ||
285 | break; | ||
286 | } | ||
287 | |||
288 | return (0); | ||
289 | } | ||
290 | |||
291 | static u32 acpi_ec_smb_func(struct i2c_adapter *adapter) | ||
292 | { | ||
293 | ACPI_FUNCTION_TRACE("acpi_ec_smb_func"); | ||
294 | |||
295 | return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | ||
296 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | | ||
297 | I2C_FUNC_SMBUS_BLOCK_DATA | | ||
298 | I2C_FUNC_SMBUS_PROC_CALL | | ||
299 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
300 | I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC); | ||
301 | } | ||
302 | |||
303 | static struct i2c_algorithm acpi_ec_smbus_algorithm = { | ||
304 | .smbus_xfer = acpi_ec_smb_access, | ||
305 | .functionality = acpi_ec_smb_func, | ||
306 | }; | ||
307 | |||
308 | static int acpi_ec_hc_add(struct acpi_device *device) | ||
309 | { | ||
310 | int status; | ||
311 | unsigned long val; | ||
312 | struct acpi_ec_hc *ec_hc; | ||
313 | struct acpi_ec_smbus *smbus; | ||
314 | |||
315 | ACPI_FUNCTION_TRACE("acpi_ec_hc_add"); | ||
316 | |||
317 | if (!device) { | ||
318 | return_VALUE(-EINVAL); | ||
319 | } | ||
320 | |||
321 | ec_hc = kmalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL); | ||
322 | if (!ec_hc) { | ||
323 | return_VALUE(-ENOMEM); | ||
324 | } | ||
325 | memset(ec_hc, 0, sizeof(struct acpi_ec_hc)); | ||
326 | |||
327 | smbus = kmalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL); | ||
328 | if (!smbus) { | ||
329 | kfree(ec_hc); | ||
330 | return_VALUE(-ENOMEM); | ||
331 | } | ||
332 | memset(smbus, 0, sizeof(struct acpi_ec_smbus)); | ||
333 | |||
334 | ec_hc->handle = device->handle; | ||
335 | strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME); | ||
336 | strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS); | ||
337 | acpi_driver_data(device) = ec_hc; | ||
338 | |||
339 | status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val); | ||
340 | if (ACPI_FAILURE(status)) { | ||
341 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n")); | ||
342 | kfree(ec_hc->smbus); | ||
343 | kfree(smbus); | ||
344 | return_VALUE(-EIO); | ||
345 | } | ||
346 | |||
347 | smbus->ec = acpi_driver_data(device->parent); | ||
348 | smbus->base = (val & 0xff00ull) >> 8; | ||
349 | smbus->alert = val & 0xffull; | ||
350 | |||
351 | smbus->adapter.owner = THIS_MODULE; | ||
352 | smbus->adapter.algo = &acpi_ec_smbus_algorithm; | ||
353 | smbus->adapter.algo_data = smbus; | ||
354 | |||
355 | if (i2c_add_adapter(&smbus->adapter)) { | ||
356 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, | ||
357 | "EC SMBus adapter: Failed to register adapter\n")); | ||
358 | kfree(smbus); | ||
359 | kfree(ec_hc); | ||
360 | return_VALUE(-EIO); | ||
361 | } | ||
362 | |||
363 | ec_hc->smbus = smbus; | ||
364 | |||
365 | printk(KERN_INFO PREFIX "%s [%s]\n", | ||
366 | acpi_device_name(device), acpi_device_bid(device)); | ||
367 | |||
368 | return_VALUE(AE_OK); | ||
369 | } | ||
370 | |||
371 | static int acpi_ec_hc_remove(struct acpi_device *device, int type) | ||
372 | { | ||
373 | struct acpi_ec_hc *ec_hc; | ||
374 | |||
375 | ACPI_FUNCTION_TRACE("acpi_ec_hc_remove"); | ||
376 | |||
377 | if (!device) { | ||
378 | return_VALUE(-EINVAL); | ||
379 | } | ||
380 | ec_hc = acpi_driver_data(device); | ||
381 | |||
382 | i2c_del_adapter(&ec_hc->smbus->adapter); | ||
383 | kfree(ec_hc->smbus); | ||
384 | kfree(ec_hc); | ||
385 | |||
386 | return_VALUE(AE_OK); | ||
387 | } | ||
388 | |||
389 | static int __init acpi_ec_hc_init(void) | ||
390 | { | ||
391 | int result; | ||
392 | |||
393 | ACPI_FUNCTION_TRACE("acpi_ec_hc_init"); | ||
394 | result = acpi_bus_register_driver(&acpi_ec_hc_driver); | ||
395 | if (result < 0) { | ||
396 | return_VALUE(-ENODEV); | ||
397 | } | ||
398 | return_VALUE(0); | ||
399 | } | ||
400 | |||
401 | static void __exit acpi_ec_hc_exit(void) | ||
402 | { | ||
403 | ACPI_FUNCTION_TRACE("acpi_ec_hc_exit"); | ||
404 | acpi_bus_unregister_driver(&acpi_ec_hc_driver); | ||
405 | } | ||
406 | |||
407 | struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device) | ||
408 | { | ||
409 | ACPI_FUNCTION_TRACE("acpi_get_ec_hc"); | ||
410 | return ((struct acpi_ec_hc *)acpi_driver_data(device->parent)); | ||
411 | } | ||
412 | |||
413 | EXPORT_SYMBOL(acpi_get_ec_hc); | ||
414 | |||
415 | module_init(acpi_ec_hc_init); | ||
416 | module_exit(acpi_ec_hc_exit); | ||
417 | |||
418 | MODULE_LICENSE("GPL"); | ||
419 | MODULE_AUTHOR("Ducrot Bruno"); | ||
420 | MODULE_DESCRIPTION("ACPI EC SMBus driver"); | ||
diff --git a/drivers/acpi/i2c_ec.h b/drivers/acpi/i2c_ec.h new file mode 100644 index 000000000000..7c53fb732d61 --- /dev/null +++ b/drivers/acpi/i2c_ec.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $) | ||
3 | * | ||
4 | * Copyright (c) 2002, 2005 Ducrot Bruno | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation version 2. | ||
9 | */ | ||
10 | |||
11 | struct acpi_ec_smbus { | ||
12 | struct i2c_adapter adapter; | ||
13 | union acpi_ec *ec; | ||
14 | int base; | ||
15 | int alert; | ||
16 | }; | ||
17 | |||
18 | struct acpi_ec_hc { | ||
19 | acpi_handle handle; | ||
20 | struct acpi_ec_smbus *smbus; | ||
21 | }; | ||
22 | |||
23 | struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device); | ||
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c new file mode 100644 index 000000000000..8bebcebff5f0 --- /dev/null +++ b/drivers/acpi/sbs.c | |||
@@ -0,0 +1,1828 @@ | |||
1 | /* | ||
2 | * acpi_sbs.c - ACPI Smart Battery System Driver ($Revision: 1.16 $) | ||
3 | * | ||
4 | * Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu> | ||
5 | * | ||
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
21 | * | ||
22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
23 | */ | ||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/proc_fs.h> | ||
30 | #include <linux/seq_file.h> | ||
31 | #include <asm/uaccess.h> | ||
32 | #include <linux/acpi.h> | ||
33 | #include <linux/i2c.h> | ||
34 | #include <linux/delay.h> | ||
35 | |||
36 | #include "i2c_ec.h" | ||
37 | |||
38 | #define DEF_CAPACITY_UNIT 3 | ||
39 | #define MAH_CAPACITY_UNIT 1 | ||
40 | #define MWH_CAPACITY_UNIT 2 | ||
41 | #define CAPACITY_UNIT DEF_CAPACITY_UNIT | ||
42 | |||
43 | #define REQUEST_UPDATE_MODE 1 | ||
44 | #define QUEUE_UPDATE_MODE 2 | ||
45 | |||
46 | #define DATA_TYPE_COMMON 0 | ||
47 | #define DATA_TYPE_INFO 1 | ||
48 | #define DATA_TYPE_STATE 2 | ||
49 | #define DATA_TYPE_ALARM 3 | ||
50 | #define DATA_TYPE_AC_STATE 4 | ||
51 | |||
52 | extern struct proc_dir_entry *acpi_lock_ac_dir(void); | ||
53 | extern struct proc_dir_entry *acpi_lock_battery_dir(void); | ||
54 | extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); | ||
55 | extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); | ||
56 | |||
57 | #define ACPI_SBS_COMPONENT 0x00080000 | ||
58 | #define ACPI_SBS_CLASS "sbs" | ||
59 | #define ACPI_AC_CLASS "ac_adapter" | ||
60 | #define ACPI_BATTERY_CLASS "battery" | ||
61 | #define ACPI_SBS_HID "ACPI0002" | ||
62 | #define ACPI_SBS_DRIVER_NAME "ACPI Smart Battery System Driver" | ||
63 | #define ACPI_SBS_DEVICE_NAME "Smart Battery System" | ||
64 | #define ACPI_SBS_FILE_INFO "info" | ||
65 | #define ACPI_SBS_FILE_STATE "state" | ||
66 | #define ACPI_SBS_FILE_ALARM "alarm" | ||
67 | #define ACPI_BATTERY_DIR_NAME "BAT%i" | ||
68 | #define ACPI_AC_DIR_NAME "AC0" | ||
69 | #define ACPI_SBC_SMBUS_ADDR 0x9 | ||
70 | #define ACPI_SBSM_SMBUS_ADDR 0xa | ||
71 | #define ACPI_SB_SMBUS_ADDR 0xb | ||
72 | #define ACPI_SBS_AC_NOTIFY_STATUS 0x80 | ||
73 | #define ACPI_SBS_BATTERY_NOTIFY_STATUS 0x80 | ||
74 | #define ACPI_SBS_BATTERY_NOTIFY_INFO 0x81 | ||
75 | |||
76 | #define _COMPONENT ACPI_SBS_COMPONENT | ||
77 | |||
78 | #define MAX_SBS_BAT 4 | ||
79 | #define MAX_SMBUS_ERR 1 | ||
80 | |||
81 | ACPI_MODULE_NAME("acpi_sbs"); | ||
82 | |||
83 | MODULE_AUTHOR("Rich Townsend"); | ||
84 | MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); | ||
85 | MODULE_LICENSE("GPL"); | ||
86 | |||
87 | static struct semaphore sbs_sem; | ||
88 | |||
89 | #define UPDATE_MODE QUEUE_UPDATE_MODE | ||
90 | /* REQUEST_UPDATE_MODE QUEUE_UPDATE_MODE */ | ||
91 | #define UPDATE_INFO_MODE 0 | ||
92 | #define UPDATE_TIME 60 | ||
93 | #define UPDATE_TIME2 0 | ||
94 | |||
95 | static int capacity_mode = CAPACITY_UNIT; | ||
96 | static int update_mode = UPDATE_MODE; | ||
97 | static int update_info_mode = UPDATE_INFO_MODE; | ||
98 | static int update_time = UPDATE_TIME; | ||
99 | static int update_time2 = UPDATE_TIME2; | ||
100 | |||
101 | module_param(capacity_mode, int, CAPACITY_UNIT); | ||
102 | module_param(update_mode, int, UPDATE_MODE); | ||
103 | module_param(update_info_mode, int, UPDATE_INFO_MODE); | ||
104 | module_param(update_time, int, UPDATE_TIME); | ||
105 | module_param(update_time2, int, UPDATE_TIME2); | ||
106 | |||
107 | static int acpi_sbs_add(struct acpi_device *device); | ||
108 | static int acpi_sbs_remove(struct acpi_device *device, int type); | ||
109 | static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus); | ||
110 | static void acpi_sbs_update_queue(void *data); | ||
111 | |||
112 | static struct acpi_driver acpi_sbs_driver = { | ||
113 | .name = ACPI_SBS_DRIVER_NAME, | ||
114 | .class = ACPI_SBS_CLASS, | ||
115 | .ids = ACPI_SBS_HID, | ||
116 | .ops = { | ||
117 | .add = acpi_sbs_add, | ||
118 | .remove = acpi_sbs_remove, | ||
119 | }, | ||
120 | }; | ||
121 | |||
122 | struct acpi_battery_info { | ||
123 | int capacity_mode; | ||
124 | s16 full_charge_capacity; | ||
125 | s16 design_capacity; | ||
126 | s16 design_voltage; | ||
127 | int vscale; | ||
128 | int ipscale; | ||
129 | s16 serial_number; | ||
130 | char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3]; | ||
131 | char device_name[I2C_SMBUS_BLOCK_MAX + 3]; | ||
132 | char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3]; | ||
133 | }; | ||
134 | |||
135 | struct acpi_battery_state { | ||
136 | s16 voltage; | ||
137 | s16 amperage; | ||
138 | s16 remaining_capacity; | ||
139 | s16 average_time_to_empty; | ||
140 | s16 average_time_to_full; | ||
141 | s16 battery_status; | ||
142 | }; | ||
143 | |||
144 | struct acpi_battery_alarm { | ||
145 | s16 remaining_capacity; | ||
146 | }; | ||
147 | |||
148 | struct acpi_battery { | ||
149 | int alive; | ||
150 | int battery_present; | ||
151 | int id; | ||
152 | int init_state; | ||
153 | struct acpi_sbs *sbs; | ||
154 | struct acpi_battery_info info; | ||
155 | struct acpi_battery_state state; | ||
156 | struct acpi_battery_alarm alarm; | ||
157 | struct proc_dir_entry *battery_entry; | ||
158 | }; | ||
159 | |||
160 | struct acpi_sbs { | ||
161 | acpi_handle handle; | ||
162 | struct acpi_device *device; | ||
163 | struct acpi_ec_smbus *smbus; | ||
164 | int sbsm_present; | ||
165 | int sbsm_batteries_supported; | ||
166 | int ac_present; | ||
167 | struct proc_dir_entry *ac_entry; | ||
168 | struct acpi_battery battery[MAX_SBS_BAT]; | ||
169 | int update_info_mode; | ||
170 | int zombie; | ||
171 | int update_time; | ||
172 | int update_time2; | ||
173 | struct timer_list update_timer; | ||
174 | }; | ||
175 | |||
176 | static void acpi_update_delay(struct acpi_sbs *sbs); | ||
177 | static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type); | ||
178 | |||
179 | /* -------------------------------------------------------------------------- | ||
180 | SMBus Communication | ||
181 | -------------------------------------------------------------------------- */ | ||
182 | |||
183 | static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus) | ||
184 | { | ||
185 | union i2c_smbus_data data; | ||
186 | int result = 0; | ||
187 | char *err_str; | ||
188 | int err_number; | ||
189 | |||
190 | ACPI_FUNCTION_TRACE("acpi_battery_smbus_err_handler"); | ||
191 | |||
192 | data.word = 0; | ||
193 | |||
194 | result = smbus->adapter.algo-> | ||
195 | smbus_xfer(&smbus->adapter, | ||
196 | ACPI_SB_SMBUS_ADDR, | ||
197 | 0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data); | ||
198 | |||
199 | err_number = (data.word & 0x000f); | ||
200 | |||
201 | switch (data.word & 0x000f) { | ||
202 | case 0x0000: | ||
203 | err_str = "unexpected bus error"; | ||
204 | break; | ||
205 | case 0x0001: | ||
206 | err_str = "busy"; | ||
207 | break; | ||
208 | case 0x0002: | ||
209 | err_str = "reserved command"; | ||
210 | break; | ||
211 | case 0x0003: | ||
212 | err_str = "unsupported command"; | ||
213 | break; | ||
214 | case 0x0004: | ||
215 | err_str = "access denied"; | ||
216 | break; | ||
217 | case 0x0005: | ||
218 | err_str = "overflow/underflow"; | ||
219 | break; | ||
220 | case 0x0006: | ||
221 | err_str = "bad size"; | ||
222 | break; | ||
223 | case 0x0007: | ||
224 | err_str = "unknown error"; | ||
225 | break; | ||
226 | default: | ||
227 | err_str = "unrecognized error"; | ||
228 | } | ||
229 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
230 | "%s: ret %i, err %i\n", err_str, result, err_number)); | ||
231 | } | ||
232 | |||
233 | static int | ||
234 | acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func, | ||
235 | u16 * word, | ||
236 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | ||
237 | { | ||
238 | union i2c_smbus_data data; | ||
239 | int result = 0; | ||
240 | int i; | ||
241 | |||
242 | ACPI_FUNCTION_TRACE("acpi_sbs_smbus_read_word"); | ||
243 | |||
244 | if (err_handler == NULL) { | ||
245 | err_handler = acpi_battery_smbus_err_handler; | ||
246 | } | ||
247 | |||
248 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | ||
249 | result = | ||
250 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | ||
251 | I2C_SMBUS_READ, func, | ||
252 | I2C_SMBUS_WORD_DATA, &data); | ||
253 | if (result) { | ||
254 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
255 | "try %i: smbus->adapter.algo->smbus_xfer() failed\n", | ||
256 | i)); | ||
257 | if (err_handler) { | ||
258 | err_handler(smbus); | ||
259 | } | ||
260 | } else { | ||
261 | *word = data.word; | ||
262 | break; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | return_VALUE(result); | ||
267 | } | ||
268 | |||
269 | static int | ||
270 | acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func, | ||
271 | char *str, | ||
272 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | ||
273 | { | ||
274 | union i2c_smbus_data data; | ||
275 | int result = 0; | ||
276 | int i; | ||
277 | |||
278 | ACPI_FUNCTION_TRACE("acpi_sbs_smbus_read_str"); | ||
279 | |||
280 | if (err_handler == NULL) { | ||
281 | err_handler = acpi_battery_smbus_err_handler; | ||
282 | } | ||
283 | |||
284 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | ||
285 | result = | ||
286 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | ||
287 | I2C_SMBUS_READ, func, | ||
288 | I2C_SMBUS_BLOCK_DATA, | ||
289 | &data); | ||
290 | if (result) { | ||
291 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
292 | "try %i: smbus->adapter.algo->smbus_xfer() failed\n", | ||
293 | i)); | ||
294 | if (err_handler) { | ||
295 | err_handler(smbus); | ||
296 | } | ||
297 | } else { | ||
298 | strncpy(str, (const char *)data.block + 1, | ||
299 | data.block[0]); | ||
300 | str[data.block[0]] = 0; | ||
301 | break; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | return_VALUE(result); | ||
306 | } | ||
307 | |||
308 | static int | ||
309 | acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func, | ||
310 | int word, | ||
311 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | ||
312 | { | ||
313 | union i2c_smbus_data data; | ||
314 | int result = 0; | ||
315 | int i; | ||
316 | |||
317 | ACPI_FUNCTION_TRACE("acpi_sbs_smbus_write_word"); | ||
318 | |||
319 | if (err_handler == NULL) { | ||
320 | err_handler = acpi_battery_smbus_err_handler; | ||
321 | } | ||
322 | |||
323 | data.word = word; | ||
324 | |||
325 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | ||
326 | result = | ||
327 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | ||
328 | I2C_SMBUS_WRITE, func, | ||
329 | I2C_SMBUS_WORD_DATA, &data); | ||
330 | if (result) { | ||
331 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
332 | "try %i: smbus->adapter.algo" | ||
333 | "->smbus_xfer() failed\n", i)); | ||
334 | if (err_handler) { | ||
335 | err_handler(smbus); | ||
336 | } | ||
337 | } else { | ||
338 | break; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | return_VALUE(result); | ||
343 | } | ||
344 | |||
345 | /* -------------------------------------------------------------------------- | ||
346 | Smart Battery System Management | ||
347 | -------------------------------------------------------------------------- */ | ||
348 | |||
349 | /* Smart Battery */ | ||
350 | |||
351 | static int acpi_sbs_generate_event(struct acpi_device *device, | ||
352 | int event, int state, char *bid, char *class) | ||
353 | { | ||
354 | char bid_saved[5]; | ||
355 | char class_saved[20]; | ||
356 | int result = 0; | ||
357 | |||
358 | ACPI_FUNCTION_TRACE("acpi_sbs_generate_event"); | ||
359 | |||
360 | strcpy(bid_saved, acpi_device_bid(device)); | ||
361 | strcpy(class_saved, acpi_device_class(device)); | ||
362 | |||
363 | strcpy(acpi_device_bid(device), bid); | ||
364 | strcpy(acpi_device_class(device), class); | ||
365 | |||
366 | result = acpi_bus_generate_event(device, event, state); | ||
367 | |||
368 | strcpy(acpi_device_bid(device), bid_saved); | ||
369 | strcpy(acpi_device_class(device), class_saved); | ||
370 | |||
371 | return_VALUE(result); | ||
372 | } | ||
373 | |||
374 | static int acpi_battery_get_present(struct acpi_battery *battery) | ||
375 | { | ||
376 | s16 state; | ||
377 | int result = 0; | ||
378 | int is_present = 0; | ||
379 | |||
380 | ACPI_FUNCTION_TRACE("acpi_battery_get_present"); | ||
381 | |||
382 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, | ||
383 | ACPI_SBSM_SMBUS_ADDR, 0x01, | ||
384 | &state, NULL); | ||
385 | if (result) { | ||
386 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
387 | "acpi_sbs_smbus_read_word() failed")); | ||
388 | } | ||
389 | if (!result) { | ||
390 | is_present = (state & 0x000f) & (1 << battery->id); | ||
391 | } | ||
392 | battery->battery_present = is_present; | ||
393 | |||
394 | return_VALUE(result); | ||
395 | } | ||
396 | |||
397 | static int acpi_battery_is_present(struct acpi_battery *battery) | ||
398 | { | ||
399 | return (battery->battery_present); | ||
400 | } | ||
401 | |||
402 | static int acpi_ac_is_present(struct acpi_sbs *sbs) | ||
403 | { | ||
404 | return (sbs->ac_present); | ||
405 | } | ||
406 | |||
407 | static int acpi_battery_select(struct acpi_battery *battery) | ||
408 | { | ||
409 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | ||
410 | int result = 0; | ||
411 | s16 state; | ||
412 | int foo; | ||
413 | |||
414 | ACPI_FUNCTION_TRACE("acpi_battery_select"); | ||
415 | |||
416 | if (battery->sbs->sbsm_present) { | ||
417 | |||
418 | /* Take special care not to knobble other nibbles of | ||
419 | * state (aka selector_state), since | ||
420 | * it causes charging to halt on SBSELs */ | ||
421 | |||
422 | result = | ||
423 | acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, | ||
424 | &state, NULL); | ||
425 | if (result) { | ||
426 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
427 | "acpi_sbs_smbus_read_word() failed\n")); | ||
428 | goto end; | ||
429 | } | ||
430 | |||
431 | foo = (state & 0x0fff) | (1 << (battery->id + 12)); | ||
432 | result = | ||
433 | acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, | ||
434 | foo, NULL); | ||
435 | if (result) { | ||
436 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
437 | "acpi_sbs_smbus_write_word() failed\n")); | ||
438 | goto end; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | end: | ||
443 | return_VALUE(result); | ||
444 | } | ||
445 | |||
446 | static int acpi_sbsm_get_info(struct acpi_sbs *sbs) | ||
447 | { | ||
448 | struct acpi_ec_smbus *smbus = sbs->smbus; | ||
449 | int result = 0; | ||
450 | s16 battery_system_info; | ||
451 | |||
452 | ACPI_FUNCTION_TRACE("acpi_sbsm_get_info"); | ||
453 | |||
454 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, | ||
455 | &battery_system_info, NULL); | ||
456 | if (result) { | ||
457 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
458 | "acpi_sbs_smbus_read_word() failed\n")); | ||
459 | goto end; | ||
460 | } | ||
461 | |||
462 | sbs->sbsm_batteries_supported = battery_system_info & 0x000f; | ||
463 | |||
464 | end: | ||
465 | |||
466 | return_VALUE(result); | ||
467 | } | ||
468 | |||
469 | static int acpi_battery_get_info(struct acpi_battery *battery) | ||
470 | { | ||
471 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | ||
472 | int result = 0; | ||
473 | s16 battery_mode; | ||
474 | s16 specification_info; | ||
475 | |||
476 | ACPI_FUNCTION_TRACE("acpi_battery_get_info"); | ||
477 | |||
478 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, | ||
479 | &battery_mode, | ||
480 | &acpi_battery_smbus_err_handler); | ||
481 | if (result) { | ||
482 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
483 | "acpi_sbs_smbus_read_word() failed\n")); | ||
484 | goto end; | ||
485 | } | ||
486 | battery->info.capacity_mode = (battery_mode & 0x8000) >> 15; | ||
487 | |||
488 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10, | ||
489 | &battery->info.full_charge_capacity, | ||
490 | &acpi_battery_smbus_err_handler); | ||
491 | if (result) { | ||
492 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
493 | "acpi_sbs_smbus_read_word() failed\n")); | ||
494 | goto end; | ||
495 | } | ||
496 | |||
497 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18, | ||
498 | &battery->info.design_capacity, | ||
499 | &acpi_battery_smbus_err_handler); | ||
500 | |||
501 | if (result) { | ||
502 | goto end; | ||
503 | } | ||
504 | |||
505 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19, | ||
506 | &battery->info.design_voltage, | ||
507 | &acpi_battery_smbus_err_handler); | ||
508 | if (result) { | ||
509 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
510 | "acpi_sbs_smbus_read_word() failed\n")); | ||
511 | goto end; | ||
512 | } | ||
513 | |||
514 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a, | ||
515 | &specification_info, | ||
516 | &acpi_battery_smbus_err_handler); | ||
517 | if (result) { | ||
518 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
519 | "acpi_sbs_smbus_read_word() failed\n")); | ||
520 | goto end; | ||
521 | } | ||
522 | |||
523 | switch ((specification_info & 0x0f00) >> 8) { | ||
524 | case 1: | ||
525 | battery->info.vscale = 10; | ||
526 | break; | ||
527 | case 2: | ||
528 | battery->info.vscale = 100; | ||
529 | break; | ||
530 | case 3: | ||
531 | battery->info.vscale = 1000; | ||
532 | break; | ||
533 | default: | ||
534 | battery->info.vscale = 1; | ||
535 | } | ||
536 | |||
537 | switch ((specification_info & 0xf000) >> 12) { | ||
538 | case 1: | ||
539 | battery->info.ipscale = 10; | ||
540 | break; | ||
541 | case 2: | ||
542 | battery->info.ipscale = 100; | ||
543 | break; | ||
544 | case 3: | ||
545 | battery->info.ipscale = 1000; | ||
546 | break; | ||
547 | default: | ||
548 | battery->info.ipscale = 1; | ||
549 | } | ||
550 | |||
551 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c, | ||
552 | &battery->info.serial_number, | ||
553 | &acpi_battery_smbus_err_handler); | ||
554 | if (result) { | ||
555 | goto end; | ||
556 | } | ||
557 | |||
558 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20, | ||
559 | battery->info.manufacturer_name, | ||
560 | &acpi_battery_smbus_err_handler); | ||
561 | if (result) { | ||
562 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
563 | "acpi_sbs_smbus_read_str() failed\n")); | ||
564 | goto end; | ||
565 | } | ||
566 | |||
567 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21, | ||
568 | battery->info.device_name, | ||
569 | &acpi_battery_smbus_err_handler); | ||
570 | if (result) { | ||
571 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
572 | "acpi_sbs_smbus_read_str() failed\n")); | ||
573 | goto end; | ||
574 | } | ||
575 | |||
576 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22, | ||
577 | battery->info.device_chemistry, | ||
578 | &acpi_battery_smbus_err_handler); | ||
579 | if (result) { | ||
580 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
581 | "acpi_sbs_smbus_read_str() failed\n")); | ||
582 | goto end; | ||
583 | } | ||
584 | |||
585 | end: | ||
586 | return_VALUE(result); | ||
587 | } | ||
588 | |||
589 | static void acpi_update_delay(struct acpi_sbs *sbs) | ||
590 | { | ||
591 | ACPI_FUNCTION_TRACE("acpi_update_delay"); | ||
592 | if (sbs->zombie) { | ||
593 | return; | ||
594 | } | ||
595 | if (sbs->update_time2 > 0) { | ||
596 | msleep(sbs->update_time2 * 1000); | ||
597 | } | ||
598 | } | ||
599 | |||
600 | static int acpi_battery_get_state(struct acpi_battery *battery) | ||
601 | { | ||
602 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | ||
603 | int result = 0; | ||
604 | |||
605 | ACPI_FUNCTION_TRACE("acpi_battery_get_state"); | ||
606 | |||
607 | acpi_update_delay(battery->sbs); | ||
608 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09, | ||
609 | &battery->state.voltage, | ||
610 | &acpi_battery_smbus_err_handler); | ||
611 | if (result) { | ||
612 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
613 | "acpi_sbs_smbus_read_word() failed\n")); | ||
614 | goto end; | ||
615 | } | ||
616 | |||
617 | acpi_update_delay(battery->sbs); | ||
618 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a, | ||
619 | &battery->state.amperage, | ||
620 | &acpi_battery_smbus_err_handler); | ||
621 | if (result) { | ||
622 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
623 | "acpi_sbs_smbus_read_word() failed\n")); | ||
624 | goto end; | ||
625 | } | ||
626 | |||
627 | acpi_update_delay(battery->sbs); | ||
628 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f, | ||
629 | &battery->state.remaining_capacity, | ||
630 | &acpi_battery_smbus_err_handler); | ||
631 | if (result) { | ||
632 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
633 | "acpi_sbs_smbus_read_word() failed\n")); | ||
634 | goto end; | ||
635 | } | ||
636 | |||
637 | acpi_update_delay(battery->sbs); | ||
638 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12, | ||
639 | &battery->state.average_time_to_empty, | ||
640 | &acpi_battery_smbus_err_handler); | ||
641 | if (result) { | ||
642 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
643 | "acpi_sbs_smbus_read_word() failed\n")); | ||
644 | goto end; | ||
645 | } | ||
646 | |||
647 | acpi_update_delay(battery->sbs); | ||
648 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13, | ||
649 | &battery->state.average_time_to_full, | ||
650 | &acpi_battery_smbus_err_handler); | ||
651 | if (result) { | ||
652 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
653 | "acpi_sbs_smbus_read_word() failed\n")); | ||
654 | goto end; | ||
655 | } | ||
656 | |||
657 | acpi_update_delay(battery->sbs); | ||
658 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16, | ||
659 | &battery->state.battery_status, | ||
660 | &acpi_battery_smbus_err_handler); | ||
661 | if (result) { | ||
662 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
663 | "acpi_sbs_smbus_read_word() failed\n")); | ||
664 | goto end; | ||
665 | } | ||
666 | |||
667 | acpi_update_delay(battery->sbs); | ||
668 | |||
669 | end: | ||
670 | return_VALUE(result); | ||
671 | } | ||
672 | |||
673 | static int acpi_battery_get_alarm(struct acpi_battery *battery) | ||
674 | { | ||
675 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | ||
676 | int result = 0; | ||
677 | |||
678 | ACPI_FUNCTION_TRACE("acpi_battery_get_alarm"); | ||
679 | |||
680 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, | ||
681 | &battery->alarm.remaining_capacity, | ||
682 | &acpi_battery_smbus_err_handler); | ||
683 | if (result) { | ||
684 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
685 | "acpi_sbs_smbus_read_word() failed\n")); | ||
686 | goto end; | ||
687 | } | ||
688 | |||
689 | acpi_update_delay(battery->sbs); | ||
690 | |||
691 | end: | ||
692 | |||
693 | return_VALUE(result); | ||
694 | } | ||
695 | |||
696 | static int acpi_battery_set_alarm(struct acpi_battery *battery, | ||
697 | unsigned long alarm) | ||
698 | { | ||
699 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | ||
700 | int result = 0; | ||
701 | s16 battery_mode; | ||
702 | int foo; | ||
703 | |||
704 | ACPI_FUNCTION_TRACE("acpi_battery_set_alarm"); | ||
705 | |||
706 | result = acpi_battery_select(battery); | ||
707 | if (result) { | ||
708 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
709 | "acpi_battery_select() failed\n")); | ||
710 | goto end; | ||
711 | } | ||
712 | |||
713 | /* If necessary, enable the alarm */ | ||
714 | |||
715 | if (alarm > 0) { | ||
716 | result = | ||
717 | acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, | ||
718 | &battery_mode, | ||
719 | &acpi_battery_smbus_err_handler); | ||
720 | if (result) { | ||
721 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
722 | "acpi_sbs_smbus_read_word() failed\n")); | ||
723 | goto end; | ||
724 | } | ||
725 | |||
726 | result = | ||
727 | acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, | ||
728 | battery_mode & 0xbfff, | ||
729 | &acpi_battery_smbus_err_handler); | ||
730 | if (result) { | ||
731 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
732 | "acpi_sbs_smbus_write_word() failed\n")); | ||
733 | goto end; | ||
734 | } | ||
735 | } | ||
736 | |||
737 | foo = alarm / (battery->info.capacity_mode ? 10 : 1); | ||
738 | result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, | ||
739 | foo, | ||
740 | &acpi_battery_smbus_err_handler); | ||
741 | if (result) { | ||
742 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
743 | "acpi_sbs_smbus_write_word() failed\n")); | ||
744 | goto end; | ||
745 | } | ||
746 | |||
747 | end: | ||
748 | |||
749 | return_VALUE(result); | ||
750 | } | ||
751 | |||
752 | static int acpi_battery_set_mode(struct acpi_battery *battery) | ||
753 | { | ||
754 | int result = 0; | ||
755 | s16 battery_mode; | ||
756 | |||
757 | ACPI_FUNCTION_TRACE("acpi_battery_set_mode"); | ||
758 | |||
759 | if (capacity_mode == DEF_CAPACITY_UNIT) { | ||
760 | goto end; | ||
761 | } | ||
762 | |||
763 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, | ||
764 | ACPI_SB_SMBUS_ADDR, 0x03, | ||
765 | &battery_mode, NULL); | ||
766 | if (result) { | ||
767 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
768 | "acpi_sbs_smbus_read_word() failed\n")); | ||
769 | goto end; | ||
770 | } | ||
771 | |||
772 | if (capacity_mode == MAH_CAPACITY_UNIT) { | ||
773 | battery_mode &= 0x7fff; | ||
774 | } else { | ||
775 | battery_mode |= 0x8000; | ||
776 | } | ||
777 | result = acpi_sbs_smbus_write_word(battery->sbs->smbus, | ||
778 | ACPI_SB_SMBUS_ADDR, 0x03, | ||
779 | battery_mode, NULL); | ||
780 | if (result) { | ||
781 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
782 | "acpi_sbs_smbus_write_word() failed\n")); | ||
783 | goto end; | ||
784 | } | ||
785 | |||
786 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, | ||
787 | ACPI_SB_SMBUS_ADDR, 0x03, | ||
788 | &battery_mode, NULL); | ||
789 | if (result) { | ||
790 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
791 | "acpi_sbs_smbus_read_word() failed\n")); | ||
792 | goto end; | ||
793 | } | ||
794 | |||
795 | end: | ||
796 | return_VALUE(result); | ||
797 | } | ||
798 | |||
799 | static int acpi_battery_init(struct acpi_battery *battery) | ||
800 | { | ||
801 | int result = 0; | ||
802 | |||
803 | ACPI_FUNCTION_TRACE("acpi_battery_init"); | ||
804 | |||
805 | result = acpi_battery_select(battery); | ||
806 | if (result) { | ||
807 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
808 | "acpi_battery_init() failed\n")); | ||
809 | goto end; | ||
810 | } | ||
811 | |||
812 | result = acpi_battery_set_mode(battery); | ||
813 | if (result) { | ||
814 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
815 | "acpi_battery_set_mode() failed\n")); | ||
816 | goto end; | ||
817 | } | ||
818 | |||
819 | result = acpi_battery_get_info(battery); | ||
820 | if (result) { | ||
821 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
822 | "acpi_battery_get_info() failed\n")); | ||
823 | goto end; | ||
824 | } | ||
825 | |||
826 | result = acpi_battery_get_state(battery); | ||
827 | if (result) { | ||
828 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
829 | "acpi_battery_get_state() failed\n")); | ||
830 | goto end; | ||
831 | } | ||
832 | |||
833 | result = acpi_battery_get_alarm(battery); | ||
834 | if (result) { | ||
835 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
836 | "acpi_battery_get_alarm() failed\n")); | ||
837 | goto end; | ||
838 | } | ||
839 | |||
840 | end: | ||
841 | return_VALUE(result); | ||
842 | } | ||
843 | |||
844 | static int acpi_ac_get_present(struct acpi_sbs *sbs) | ||
845 | { | ||
846 | struct acpi_ec_smbus *smbus = sbs->smbus; | ||
847 | int result = 0; | ||
848 | s16 charger_status; | ||
849 | |||
850 | ACPI_FUNCTION_TRACE("acpi_ac_get_present"); | ||
851 | |||
852 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13, | ||
853 | &charger_status, NULL); | ||
854 | |||
855 | if (result) { | ||
856 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
857 | "acpi_sbs_smbus_read_word() failed\n")); | ||
858 | goto end; | ||
859 | } | ||
860 | |||
861 | sbs->ac_present = (charger_status & 0x8000) >> 15; | ||
862 | |||
863 | end: | ||
864 | |||
865 | return_VALUE(result); | ||
866 | } | ||
867 | |||
868 | /* -------------------------------------------------------------------------- | ||
869 | FS Interface (/proc/acpi) | ||
870 | -------------------------------------------------------------------------- */ | ||
871 | |||
872 | /* Generic Routines */ | ||
873 | |||
874 | static int | ||
875 | acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, | ||
876 | struct proc_dir_entry *parent_dir, | ||
877 | char *dir_name, | ||
878 | struct file_operations *info_fops, | ||
879 | struct file_operations *state_fops, | ||
880 | struct file_operations *alarm_fops, void *data) | ||
881 | { | ||
882 | struct proc_dir_entry *entry = NULL; | ||
883 | |||
884 | ACPI_FUNCTION_TRACE("acpi_sbs_generic_add_fs"); | ||
885 | |||
886 | if (!*dir) { | ||
887 | *dir = proc_mkdir(dir_name, parent_dir); | ||
888 | if (!*dir) { | ||
889 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
890 | "proc_mkdir() failed\n")); | ||
891 | return_VALUE(-ENODEV); | ||
892 | } | ||
893 | (*dir)->owner = THIS_MODULE; | ||
894 | } | ||
895 | |||
896 | /* 'info' [R] */ | ||
897 | if (info_fops) { | ||
898 | entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); | ||
899 | if (!entry) { | ||
900 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
901 | "create_proc_entry() failed\n")); | ||
902 | } else { | ||
903 | entry->proc_fops = info_fops; | ||
904 | entry->data = data; | ||
905 | entry->owner = THIS_MODULE; | ||
906 | } | ||
907 | } | ||
908 | |||
909 | /* 'state' [R] */ | ||
910 | if (state_fops) { | ||
911 | entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); | ||
912 | if (!entry) { | ||
913 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
914 | "create_proc_entry() failed\n")); | ||
915 | } else { | ||
916 | entry->proc_fops = state_fops; | ||
917 | entry->data = data; | ||
918 | entry->owner = THIS_MODULE; | ||
919 | } | ||
920 | } | ||
921 | |||
922 | /* 'alarm' [R/W] */ | ||
923 | if (alarm_fops) { | ||
924 | entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir); | ||
925 | if (!entry) { | ||
926 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
927 | "create_proc_entry() failed\n")); | ||
928 | } else { | ||
929 | entry->proc_fops = alarm_fops; | ||
930 | entry->data = data; | ||
931 | entry->owner = THIS_MODULE; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | return_VALUE(0); | ||
936 | } | ||
937 | |||
938 | static void | ||
939 | acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir, | ||
940 | struct proc_dir_entry *parent_dir) | ||
941 | { | ||
942 | ACPI_FUNCTION_TRACE("acpi_sbs_generic_remove_fs"); | ||
943 | |||
944 | if (*dir) { | ||
945 | remove_proc_entry(ACPI_SBS_FILE_INFO, *dir); | ||
946 | remove_proc_entry(ACPI_SBS_FILE_STATE, *dir); | ||
947 | remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir); | ||
948 | remove_proc_entry((*dir)->name, parent_dir); | ||
949 | *dir = NULL; | ||
950 | } | ||
951 | |||
952 | } | ||
953 | |||
954 | /* Smart Battery Interface */ | ||
955 | |||
956 | static struct proc_dir_entry *acpi_battery_dir = NULL; | ||
957 | |||
958 | static int acpi_battery_read_info(struct seq_file *seq, void *offset) | ||
959 | { | ||
960 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | ||
961 | int cscale; | ||
962 | int result = 0; | ||
963 | |||
964 | ACPI_FUNCTION_TRACE("acpi_battery_read_info"); | ||
965 | |||
966 | if (battery->sbs->zombie) { | ||
967 | return_VALUE(-ENODEV); | ||
968 | } | ||
969 | |||
970 | down(&sbs_sem); | ||
971 | |||
972 | if (update_mode == REQUEST_UPDATE_MODE) { | ||
973 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO); | ||
974 | if (result) { | ||
975 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
976 | "acpi_sbs_update_run() failed\n")); | ||
977 | } | ||
978 | } | ||
979 | |||
980 | if (acpi_battery_is_present(battery)) { | ||
981 | seq_printf(seq, "present: yes\n"); | ||
982 | } else { | ||
983 | seq_printf(seq, "present: no\n"); | ||
984 | goto end; | ||
985 | } | ||
986 | |||
987 | if (battery->info.capacity_mode) { | ||
988 | cscale = battery->info.vscale * battery->info.ipscale; | ||
989 | } else { | ||
990 | cscale = battery->info.ipscale; | ||
991 | } | ||
992 | seq_printf(seq, "design capacity: %i%s", | ||
993 | battery->info.design_capacity * cscale, | ||
994 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | ||
995 | |||
996 | seq_printf(seq, "last full capacity: %i%s", | ||
997 | battery->info.full_charge_capacity * cscale, | ||
998 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | ||
999 | |||
1000 | seq_printf(seq, "battery technology: rechargeable\n"); | ||
1001 | |||
1002 | seq_printf(seq, "design voltage: %i mV\n", | ||
1003 | battery->info.design_voltage * battery->info.vscale); | ||
1004 | |||
1005 | seq_printf(seq, "design capacity warning: unknown\n"); | ||
1006 | seq_printf(seq, "design capacity low: unknown\n"); | ||
1007 | seq_printf(seq, "capacity granularity 1: unknown\n"); | ||
1008 | seq_printf(seq, "capacity granularity 2: unknown\n"); | ||
1009 | |||
1010 | seq_printf(seq, "model number: %s\n", | ||
1011 | battery->info.device_name); | ||
1012 | |||
1013 | seq_printf(seq, "serial number: %i\n", | ||
1014 | battery->info.serial_number); | ||
1015 | |||
1016 | seq_printf(seq, "battery type: %s\n", | ||
1017 | battery->info.device_chemistry); | ||
1018 | |||
1019 | seq_printf(seq, "OEM info: %s\n", | ||
1020 | battery->info.manufacturer_name); | ||
1021 | |||
1022 | end: | ||
1023 | |||
1024 | up(&sbs_sem); | ||
1025 | |||
1026 | return_VALUE(result); | ||
1027 | } | ||
1028 | |||
1029 | static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) | ||
1030 | { | ||
1031 | return single_open(file, acpi_battery_read_info, PDE(inode)->data); | ||
1032 | } | ||
1033 | |||
1034 | static int acpi_battery_read_state(struct seq_file *seq, void *offset) | ||
1035 | { | ||
1036 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | ||
1037 | int result = 0; | ||
1038 | int cscale; | ||
1039 | int foo; | ||
1040 | |||
1041 | ACPI_FUNCTION_TRACE("acpi_battery_read_state"); | ||
1042 | |||
1043 | if (battery->sbs->zombie) { | ||
1044 | return_VALUE(-ENODEV); | ||
1045 | } | ||
1046 | |||
1047 | down(&sbs_sem); | ||
1048 | |||
1049 | if (update_mode == REQUEST_UPDATE_MODE) { | ||
1050 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE); | ||
1051 | if (result) { | ||
1052 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1053 | "acpi_sbs_update_run() failed\n")); | ||
1054 | } | ||
1055 | } | ||
1056 | |||
1057 | if (acpi_battery_is_present(battery)) { | ||
1058 | seq_printf(seq, "present: yes\n"); | ||
1059 | } else { | ||
1060 | seq_printf(seq, "present: no\n"); | ||
1061 | goto end; | ||
1062 | } | ||
1063 | |||
1064 | if (battery->info.capacity_mode) { | ||
1065 | cscale = battery->info.vscale * battery->info.ipscale; | ||
1066 | } else { | ||
1067 | cscale = battery->info.ipscale; | ||
1068 | } | ||
1069 | |||
1070 | if (battery->state.battery_status & 0x0010) { | ||
1071 | seq_printf(seq, "capacity state: critical\n"); | ||
1072 | } else { | ||
1073 | seq_printf(seq, "capacity state: ok\n"); | ||
1074 | } | ||
1075 | if (battery->state.amperage < 0) { | ||
1076 | seq_printf(seq, "charging state: discharging\n"); | ||
1077 | foo = battery->state.remaining_capacity * cscale * 60 / | ||
1078 | (battery->state.average_time_to_empty == 0 ? 1 : | ||
1079 | battery->state.average_time_to_empty); | ||
1080 | seq_printf(seq, "present rate: %i%s\n", | ||
1081 | foo, battery->info.capacity_mode ? "0 mW" : " mA"); | ||
1082 | } else if (battery->state.amperage > 0) { | ||
1083 | seq_printf(seq, "charging state: charging\n"); | ||
1084 | foo = (battery->info.full_charge_capacity - | ||
1085 | battery->state.remaining_capacity) * cscale * 60 / | ||
1086 | (battery->state.average_time_to_full == 0 ? 1 : | ||
1087 | battery->state.average_time_to_full); | ||
1088 | seq_printf(seq, "present rate: %i%s\n", | ||
1089 | foo, battery->info.capacity_mode ? "0 mW" : " mA"); | ||
1090 | } else { | ||
1091 | seq_printf(seq, "charging state: charged\n"); | ||
1092 | seq_printf(seq, "present rate: 0 %s\n", | ||
1093 | battery->info.capacity_mode ? "mW" : "mA"); | ||
1094 | } | ||
1095 | |||
1096 | seq_printf(seq, "remaining capacity: %i%s", | ||
1097 | battery->state.remaining_capacity * cscale, | ||
1098 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | ||
1099 | |||
1100 | seq_printf(seq, "present voltage: %i mV\n", | ||
1101 | battery->state.voltage * battery->info.vscale); | ||
1102 | |||
1103 | end: | ||
1104 | |||
1105 | up(&sbs_sem); | ||
1106 | |||
1107 | return_VALUE(result); | ||
1108 | } | ||
1109 | |||
1110 | static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) | ||
1111 | { | ||
1112 | return single_open(file, acpi_battery_read_state, PDE(inode)->data); | ||
1113 | } | ||
1114 | |||
1115 | static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) | ||
1116 | { | ||
1117 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | ||
1118 | int result = 0; | ||
1119 | int cscale; | ||
1120 | |||
1121 | ACPI_FUNCTION_TRACE("acpi_battery_read_alarm"); | ||
1122 | |||
1123 | if (battery->sbs->zombie) { | ||
1124 | return_VALUE(-ENODEV); | ||
1125 | } | ||
1126 | |||
1127 | down(&sbs_sem); | ||
1128 | |||
1129 | if (update_mode == REQUEST_UPDATE_MODE) { | ||
1130 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM); | ||
1131 | if (result) { | ||
1132 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1133 | "acpi_sbs_update_run() failed\n")); | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | if (!acpi_battery_is_present(battery)) { | ||
1138 | seq_printf(seq, "present: no\n"); | ||
1139 | goto end; | ||
1140 | } | ||
1141 | |||
1142 | if (battery->info.capacity_mode) { | ||
1143 | cscale = battery->info.vscale * battery->info.ipscale; | ||
1144 | } else { | ||
1145 | cscale = battery->info.ipscale; | ||
1146 | } | ||
1147 | |||
1148 | seq_printf(seq, "alarm: "); | ||
1149 | if (battery->alarm.remaining_capacity) { | ||
1150 | seq_printf(seq, "%i%s", | ||
1151 | battery->alarm.remaining_capacity * cscale, | ||
1152 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | ||
1153 | } else { | ||
1154 | seq_printf(seq, "disabled\n"); | ||
1155 | } | ||
1156 | |||
1157 | end: | ||
1158 | |||
1159 | up(&sbs_sem); | ||
1160 | |||
1161 | return_VALUE(result); | ||
1162 | } | ||
1163 | |||
1164 | static ssize_t | ||
1165 | acpi_battery_write_alarm(struct file *file, const char __user * buffer, | ||
1166 | size_t count, loff_t * ppos) | ||
1167 | { | ||
1168 | struct seq_file *seq = (struct seq_file *)file->private_data; | ||
1169 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | ||
1170 | char alarm_string[12] = { '\0' }; | ||
1171 | int result, old_alarm, new_alarm; | ||
1172 | |||
1173 | ACPI_FUNCTION_TRACE("acpi_battery_write_alarm"); | ||
1174 | |||
1175 | if (battery->sbs->zombie) { | ||
1176 | return_VALUE(-ENODEV); | ||
1177 | } | ||
1178 | |||
1179 | down(&sbs_sem); | ||
1180 | |||
1181 | if (!acpi_battery_is_present(battery)) { | ||
1182 | result = -ENODEV; | ||
1183 | goto end; | ||
1184 | } | ||
1185 | |||
1186 | if (count > sizeof(alarm_string) - 1) { | ||
1187 | result = -EINVAL; | ||
1188 | goto end; | ||
1189 | } | ||
1190 | |||
1191 | if (copy_from_user(alarm_string, buffer, count)) { | ||
1192 | result = -EFAULT; | ||
1193 | goto end; | ||
1194 | } | ||
1195 | |||
1196 | alarm_string[count] = 0; | ||
1197 | |||
1198 | old_alarm = battery->alarm.remaining_capacity; | ||
1199 | new_alarm = simple_strtoul(alarm_string, NULL, 0); | ||
1200 | |||
1201 | result = acpi_battery_set_alarm(battery, new_alarm); | ||
1202 | if (result) { | ||
1203 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1204 | "acpi_battery_set_alarm() failed\n")); | ||
1205 | (void)acpi_battery_set_alarm(battery, old_alarm); | ||
1206 | goto end; | ||
1207 | } | ||
1208 | result = acpi_battery_get_alarm(battery); | ||
1209 | if (result) { | ||
1210 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1211 | "acpi_battery_get_alarm() failed\n")); | ||
1212 | (void)acpi_battery_set_alarm(battery, old_alarm); | ||
1213 | goto end; | ||
1214 | } | ||
1215 | |||
1216 | end: | ||
1217 | up(&sbs_sem); | ||
1218 | |||
1219 | if (result) { | ||
1220 | return_VALUE(result); | ||
1221 | } else { | ||
1222 | return_VALUE(count); | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) | ||
1227 | { | ||
1228 | return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); | ||
1229 | } | ||
1230 | |||
1231 | static struct file_operations acpi_battery_info_fops = { | ||
1232 | .open = acpi_battery_info_open_fs, | ||
1233 | .read = seq_read, | ||
1234 | .llseek = seq_lseek, | ||
1235 | .release = single_release, | ||
1236 | .owner = THIS_MODULE, | ||
1237 | }; | ||
1238 | |||
1239 | static struct file_operations acpi_battery_state_fops = { | ||
1240 | .open = acpi_battery_state_open_fs, | ||
1241 | .read = seq_read, | ||
1242 | .llseek = seq_lseek, | ||
1243 | .release = single_release, | ||
1244 | .owner = THIS_MODULE, | ||
1245 | }; | ||
1246 | |||
1247 | static struct file_operations acpi_battery_alarm_fops = { | ||
1248 | .open = acpi_battery_alarm_open_fs, | ||
1249 | .read = seq_read, | ||
1250 | .write = acpi_battery_write_alarm, | ||
1251 | .llseek = seq_lseek, | ||
1252 | .release = single_release, | ||
1253 | .owner = THIS_MODULE, | ||
1254 | }; | ||
1255 | |||
1256 | /* Legacy AC Adapter Interface */ | ||
1257 | |||
1258 | static struct proc_dir_entry *acpi_ac_dir = NULL; | ||
1259 | |||
1260 | static int acpi_ac_read_state(struct seq_file *seq, void *offset) | ||
1261 | { | ||
1262 | struct acpi_sbs *sbs = (struct acpi_sbs *)seq->private; | ||
1263 | int result; | ||
1264 | |||
1265 | ACPI_FUNCTION_TRACE("acpi_ac_read_state"); | ||
1266 | |||
1267 | if (sbs->zombie) { | ||
1268 | return_VALUE(-ENODEV); | ||
1269 | } | ||
1270 | |||
1271 | down(&sbs_sem); | ||
1272 | |||
1273 | if (update_mode == REQUEST_UPDATE_MODE) { | ||
1274 | result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE); | ||
1275 | if (result) { | ||
1276 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1277 | "acpi_sbs_update_run() failed\n")); | ||
1278 | } | ||
1279 | } | ||
1280 | |||
1281 | seq_printf(seq, "state: %s\n", | ||
1282 | sbs->ac_present ? "on-line" : "off-line"); | ||
1283 | |||
1284 | up(&sbs_sem); | ||
1285 | |||
1286 | return_VALUE(0); | ||
1287 | } | ||
1288 | |||
1289 | static int acpi_ac_state_open_fs(struct inode *inode, struct file *file) | ||
1290 | { | ||
1291 | return single_open(file, acpi_ac_read_state, PDE(inode)->data); | ||
1292 | } | ||
1293 | |||
1294 | static struct file_operations acpi_ac_state_fops = { | ||
1295 | .open = acpi_ac_state_open_fs, | ||
1296 | .read = seq_read, | ||
1297 | .llseek = seq_lseek, | ||
1298 | .release = single_release, | ||
1299 | .owner = THIS_MODULE, | ||
1300 | }; | ||
1301 | |||
1302 | /* -------------------------------------------------------------------------- | ||
1303 | Driver Interface | ||
1304 | -------------------------------------------------------------------------- */ | ||
1305 | |||
1306 | /* Smart Battery */ | ||
1307 | |||
1308 | static int acpi_battery_add(struct acpi_sbs *sbs, int id) | ||
1309 | { | ||
1310 | int is_present; | ||
1311 | int result; | ||
1312 | char dir_name[32]; | ||
1313 | struct acpi_battery *battery; | ||
1314 | |||
1315 | ACPI_FUNCTION_TRACE("acpi_battery_add"); | ||
1316 | |||
1317 | battery = &sbs->battery[id]; | ||
1318 | |||
1319 | battery->alive = 0; | ||
1320 | |||
1321 | battery->init_state = 0; | ||
1322 | battery->id = id; | ||
1323 | battery->sbs = sbs; | ||
1324 | |||
1325 | result = acpi_battery_select(battery); | ||
1326 | if (result) { | ||
1327 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1328 | "acpi_battery_select() failed\n")); | ||
1329 | goto end; | ||
1330 | } | ||
1331 | |||
1332 | result = acpi_battery_get_present(battery); | ||
1333 | if (result) { | ||
1334 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1335 | "acpi_battery_get_present() failed\n")); | ||
1336 | goto end; | ||
1337 | } | ||
1338 | |||
1339 | is_present = acpi_battery_is_present(battery); | ||
1340 | |||
1341 | if (is_present) { | ||
1342 | result = acpi_battery_init(battery); | ||
1343 | if (result) { | ||
1344 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1345 | "acpi_battery_init() failed\n")); | ||
1346 | goto end; | ||
1347 | } | ||
1348 | battery->init_state = 1; | ||
1349 | } | ||
1350 | |||
1351 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | ||
1352 | |||
1353 | result = acpi_sbs_generic_add_fs(&battery->battery_entry, | ||
1354 | acpi_battery_dir, | ||
1355 | dir_name, | ||
1356 | &acpi_battery_info_fops, | ||
1357 | &acpi_battery_state_fops, | ||
1358 | &acpi_battery_alarm_fops, battery); | ||
1359 | if (result) { | ||
1360 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1361 | "acpi_sbs_generic_add_fs() failed\n")); | ||
1362 | goto end; | ||
1363 | } | ||
1364 | battery->alive = 1; | ||
1365 | |||
1366 | end: | ||
1367 | return_VALUE(result); | ||
1368 | } | ||
1369 | |||
1370 | static void acpi_battery_remove(struct acpi_sbs *sbs, int id) | ||
1371 | { | ||
1372 | ACPI_FUNCTION_TRACE("acpi_battery_remove"); | ||
1373 | |||
1374 | if (sbs->battery[id].battery_entry) { | ||
1375 | acpi_sbs_generic_remove_fs(&(sbs->battery[id].battery_entry), | ||
1376 | acpi_battery_dir); | ||
1377 | } | ||
1378 | } | ||
1379 | |||
1380 | static int acpi_ac_add(struct acpi_sbs *sbs) | ||
1381 | { | ||
1382 | int result; | ||
1383 | |||
1384 | ACPI_FUNCTION_TRACE("acpi_ac_add"); | ||
1385 | |||
1386 | result = acpi_ac_get_present(sbs); | ||
1387 | if (result) { | ||
1388 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1389 | "acpi_ac_get_present() failed\n")); | ||
1390 | goto end; | ||
1391 | } | ||
1392 | |||
1393 | result = acpi_sbs_generic_add_fs(&sbs->ac_entry, | ||
1394 | acpi_ac_dir, | ||
1395 | ACPI_AC_DIR_NAME, | ||
1396 | NULL, &acpi_ac_state_fops, NULL, sbs); | ||
1397 | if (result) { | ||
1398 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1399 | "acpi_sbs_generic_add_fs() failed\n")); | ||
1400 | goto end; | ||
1401 | } | ||
1402 | |||
1403 | end: | ||
1404 | |||
1405 | return_VALUE(result); | ||
1406 | } | ||
1407 | |||
1408 | static void acpi_ac_remove(struct acpi_sbs *sbs) | ||
1409 | { | ||
1410 | ACPI_FUNCTION_TRACE("acpi_ac_remove"); | ||
1411 | |||
1412 | if (sbs->ac_entry) { | ||
1413 | acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir); | ||
1414 | } | ||
1415 | } | ||
1416 | |||
1417 | static void acpi_sbs_update_queue_run(unsigned long data) | ||
1418 | { | ||
1419 | ACPI_FUNCTION_TRACE("acpi_sbs_update_queue_run"); | ||
1420 | acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data); | ||
1421 | } | ||
1422 | |||
1423 | static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) | ||
1424 | { | ||
1425 | struct acpi_battery *battery; | ||
1426 | int result = 0; | ||
1427 | int old_ac_present; | ||
1428 | int old_battery_present; | ||
1429 | int new_ac_present; | ||
1430 | int new_battery_present; | ||
1431 | int id; | ||
1432 | char dir_name[32]; | ||
1433 | int do_battery_init, do_ac_init; | ||
1434 | s16 old_remaining_capacity; | ||
1435 | |||
1436 | ACPI_FUNCTION_TRACE("acpi_sbs_update_run"); | ||
1437 | |||
1438 | if (sbs->zombie) { | ||
1439 | goto end; | ||
1440 | } | ||
1441 | |||
1442 | old_ac_present = acpi_ac_is_present(sbs); | ||
1443 | |||
1444 | result = acpi_ac_get_present(sbs); | ||
1445 | if (result) { | ||
1446 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1447 | "acpi_ac_get_present() failed\n")); | ||
1448 | } | ||
1449 | |||
1450 | new_ac_present = acpi_ac_is_present(sbs); | ||
1451 | |||
1452 | do_ac_init = (old_ac_present != new_ac_present); | ||
1453 | |||
1454 | if (data_type == DATA_TYPE_AC_STATE) { | ||
1455 | goto end; | ||
1456 | } | ||
1457 | |||
1458 | for (id = 0; id < MAX_SBS_BAT; id++) { | ||
1459 | battery = &sbs->battery[id]; | ||
1460 | if (battery->alive == 0) { | ||
1461 | continue; | ||
1462 | } | ||
1463 | |||
1464 | old_remaining_capacity = battery->state.remaining_capacity; | ||
1465 | |||
1466 | old_battery_present = acpi_battery_is_present(battery); | ||
1467 | |||
1468 | result = acpi_battery_select(battery); | ||
1469 | if (result) { | ||
1470 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1471 | "acpi_battery_select() failed\n")); | ||
1472 | } | ||
1473 | if (sbs->zombie) { | ||
1474 | goto end; | ||
1475 | } | ||
1476 | |||
1477 | result = acpi_battery_get_present(battery); | ||
1478 | if (result) { | ||
1479 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1480 | "acpi_battery_get_present() failed\n")); | ||
1481 | } | ||
1482 | if (sbs->zombie) { | ||
1483 | goto end; | ||
1484 | } | ||
1485 | |||
1486 | new_battery_present = acpi_battery_is_present(battery); | ||
1487 | |||
1488 | do_battery_init = ((old_battery_present != new_battery_present) | ||
1489 | && new_battery_present); | ||
1490 | |||
1491 | if (sbs->zombie) { | ||
1492 | goto end; | ||
1493 | } | ||
1494 | if (do_ac_init || do_battery_init || | ||
1495 | update_info_mode || sbs->update_info_mode) { | ||
1496 | if (sbs->update_info_mode) { | ||
1497 | sbs->update_info_mode = 0; | ||
1498 | } else { | ||
1499 | sbs->update_info_mode = 1; | ||
1500 | } | ||
1501 | result = acpi_battery_init(battery); | ||
1502 | if (result) { | ||
1503 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1504 | "acpi_battery_init() " | ||
1505 | "failed\n")); | ||
1506 | } | ||
1507 | } | ||
1508 | if (data_type == DATA_TYPE_INFO) { | ||
1509 | continue; | ||
1510 | } | ||
1511 | |||
1512 | if (sbs->zombie) { | ||
1513 | goto end; | ||
1514 | } | ||
1515 | if (new_battery_present) { | ||
1516 | result = acpi_battery_get_alarm(battery); | ||
1517 | if (result) { | ||
1518 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1519 | "acpi_battery_get_alarm() " | ||
1520 | "failed\n")); | ||
1521 | } | ||
1522 | if (data_type == DATA_TYPE_ALARM) { | ||
1523 | continue; | ||
1524 | } | ||
1525 | |||
1526 | result = acpi_battery_get_state(battery); | ||
1527 | if (result) { | ||
1528 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1529 | "acpi_battery_get_state() " | ||
1530 | "failed\n")); | ||
1531 | } | ||
1532 | } | ||
1533 | if (sbs->zombie) { | ||
1534 | goto end; | ||
1535 | } | ||
1536 | if (data_type != DATA_TYPE_COMMON) { | ||
1537 | continue; | ||
1538 | } | ||
1539 | |||
1540 | if (old_battery_present != new_battery_present) { | ||
1541 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | ||
1542 | result = acpi_sbs_generate_event(sbs->device, | ||
1543 | ACPI_SBS_BATTERY_NOTIFY_STATUS, | ||
1544 | new_battery_present, | ||
1545 | dir_name, | ||
1546 | ACPI_BATTERY_CLASS); | ||
1547 | if (result) { | ||
1548 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1549 | "acpi_sbs_generate_event() " | ||
1550 | "failed\n")); | ||
1551 | } | ||
1552 | } | ||
1553 | if (old_remaining_capacity != battery->state.remaining_capacity) { | ||
1554 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | ||
1555 | result = acpi_sbs_generate_event(sbs->device, | ||
1556 | ACPI_SBS_BATTERY_NOTIFY_STATUS, | ||
1557 | new_battery_present, | ||
1558 | dir_name, | ||
1559 | ACPI_BATTERY_CLASS); | ||
1560 | if (result) { | ||
1561 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1562 | "acpi_sbs_generate_event() failed\n")); | ||
1563 | } | ||
1564 | } | ||
1565 | |||
1566 | } | ||
1567 | if (sbs->zombie) { | ||
1568 | goto end; | ||
1569 | } | ||
1570 | if (data_type != DATA_TYPE_COMMON) { | ||
1571 | goto end; | ||
1572 | } | ||
1573 | |||
1574 | if (old_ac_present != new_ac_present) { | ||
1575 | result = acpi_sbs_generate_event(sbs->device, | ||
1576 | ACPI_SBS_AC_NOTIFY_STATUS, | ||
1577 | new_ac_present, | ||
1578 | ACPI_AC_DIR_NAME, | ||
1579 | ACPI_AC_CLASS); | ||
1580 | if (result) { | ||
1581 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1582 | "acpi_sbs_generate_event() failed\n")); | ||
1583 | } | ||
1584 | } | ||
1585 | |||
1586 | end: | ||
1587 | return_VALUE(result); | ||
1588 | } | ||
1589 | |||
1590 | static void acpi_sbs_update_queue(void *data) | ||
1591 | { | ||
1592 | struct acpi_sbs *sbs = data; | ||
1593 | unsigned long delay = -1; | ||
1594 | int result; | ||
1595 | |||
1596 | ACPI_FUNCTION_TRACE("acpi_sbs_update_queue"); | ||
1597 | |||
1598 | if (sbs->zombie) { | ||
1599 | goto end; | ||
1600 | } | ||
1601 | |||
1602 | result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON); | ||
1603 | if (result) { | ||
1604 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1605 | "acpi_sbs_update_run() failed\n")); | ||
1606 | } | ||
1607 | |||
1608 | if (sbs->zombie) { | ||
1609 | goto end; | ||
1610 | } | ||
1611 | |||
1612 | if (update_mode == REQUEST_UPDATE_MODE) { | ||
1613 | goto end; | ||
1614 | } | ||
1615 | |||
1616 | delay = jiffies + HZ * update_time; | ||
1617 | sbs->update_timer.data = (unsigned long)data; | ||
1618 | sbs->update_timer.function = acpi_sbs_update_queue_run; | ||
1619 | sbs->update_timer.expires = delay; | ||
1620 | add_timer(&sbs->update_timer); | ||
1621 | end: | ||
1622 | ; | ||
1623 | } | ||
1624 | |||
1625 | static int acpi_sbs_add(struct acpi_device *device) | ||
1626 | { | ||
1627 | struct acpi_sbs *sbs = NULL; | ||
1628 | struct acpi_ec_hc *ec_hc = NULL; | ||
1629 | int result, remove_result = 0; | ||
1630 | unsigned long sbs_obj; | ||
1631 | int id, cnt; | ||
1632 | acpi_status status = AE_OK; | ||
1633 | |||
1634 | ACPI_FUNCTION_TRACE("acpi_sbs_add"); | ||
1635 | |||
1636 | sbs = kmalloc(sizeof(struct acpi_sbs), GFP_KERNEL); | ||
1637 | if (!sbs) { | ||
1638 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n")); | ||
1639 | return_VALUE(-ENOMEM); | ||
1640 | } | ||
1641 | memset(sbs, 0, sizeof(struct acpi_sbs)); | ||
1642 | |||
1643 | cnt = 0; | ||
1644 | while (cnt < 10) { | ||
1645 | cnt++; | ||
1646 | ec_hc = acpi_get_ec_hc(device); | ||
1647 | if (ec_hc) { | ||
1648 | break; | ||
1649 | } | ||
1650 | msleep(1000); | ||
1651 | } | ||
1652 | |||
1653 | if (!ec_hc) { | ||
1654 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1655 | "acpi_get_ec_hc() failed: " | ||
1656 | "NO driver found for EC HC SMBus\n")); | ||
1657 | result = -ENODEV; | ||
1658 | goto end; | ||
1659 | } | ||
1660 | |||
1661 | sbs->device = device; | ||
1662 | sbs->smbus = ec_hc->smbus; | ||
1663 | |||
1664 | strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); | ||
1665 | strcpy(acpi_device_class(device), ACPI_SBS_CLASS); | ||
1666 | acpi_driver_data(device) = sbs; | ||
1667 | |||
1668 | sbs->update_time = 0; | ||
1669 | sbs->update_time2 = 0; | ||
1670 | |||
1671 | result = acpi_ac_add(sbs); | ||
1672 | if (result) { | ||
1673 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n")); | ||
1674 | goto end; | ||
1675 | } | ||
1676 | result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); | ||
1677 | if (ACPI_FAILURE(result)) { | ||
1678 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1679 | "acpi_evaluate_integer() failed\n")); | ||
1680 | result = -EIO; | ||
1681 | goto end; | ||
1682 | } | ||
1683 | |||
1684 | if (sbs_obj > 0) { | ||
1685 | result = acpi_sbsm_get_info(sbs); | ||
1686 | if (result) { | ||
1687 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1688 | "acpi_sbsm_get_info() failed\n")); | ||
1689 | goto end; | ||
1690 | } | ||
1691 | sbs->sbsm_present = 1; | ||
1692 | } | ||
1693 | if (sbs->sbsm_present == 0) { | ||
1694 | result = acpi_battery_add(sbs, 0); | ||
1695 | if (result) { | ||
1696 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1697 | "acpi_battery_add() failed\n")); | ||
1698 | goto end; | ||
1699 | } | ||
1700 | } else { | ||
1701 | for (id = 0; id < MAX_SBS_BAT; id++) { | ||
1702 | if ((sbs->sbsm_batteries_supported & (1 << id))) { | ||
1703 | result = acpi_battery_add(sbs, id); | ||
1704 | if (result) { | ||
1705 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1706 | "acpi_battery_add() " | ||
1707 | "failed\n")); | ||
1708 | goto end; | ||
1709 | } | ||
1710 | } | ||
1711 | } | ||
1712 | } | ||
1713 | |||
1714 | sbs->handle = device->handle; | ||
1715 | |||
1716 | init_timer(&sbs->update_timer); | ||
1717 | if (update_mode == QUEUE_UPDATE_MODE) { | ||
1718 | status = acpi_os_execute(OSL_GPE_HANDLER, | ||
1719 | acpi_sbs_update_queue, (void *)sbs); | ||
1720 | if (status != AE_OK) { | ||
1721 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1722 | "acpi_os_execute() failed\n")); | ||
1723 | } | ||
1724 | } | ||
1725 | sbs->update_time = update_time; | ||
1726 | sbs->update_time2 = update_time2; | ||
1727 | |||
1728 | printk(KERN_INFO PREFIX "%s [%s]\n", | ||
1729 | acpi_device_name(device), acpi_device_bid(device)); | ||
1730 | |||
1731 | end: | ||
1732 | if (result) { | ||
1733 | remove_result = acpi_sbs_remove(device, 0); | ||
1734 | if (remove_result) { | ||
1735 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1736 | "acpi_sbs_remove() failed\n")); | ||
1737 | } | ||
1738 | } | ||
1739 | |||
1740 | return_VALUE(result); | ||
1741 | } | ||
1742 | |||
1743 | int acpi_sbs_remove(struct acpi_device *device, int type) | ||
1744 | { | ||
1745 | struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); | ||
1746 | int id; | ||
1747 | |||
1748 | ACPI_FUNCTION_TRACE("acpi_sbs_remove"); | ||
1749 | |||
1750 | if (!device || !sbs) { | ||
1751 | return_VALUE(-EINVAL); | ||
1752 | } | ||
1753 | |||
1754 | sbs->zombie = 1; | ||
1755 | sbs->update_time = 0; | ||
1756 | sbs->update_time2 = 0; | ||
1757 | del_timer_sync(&sbs->update_timer); | ||
1758 | acpi_os_wait_events_complete(NULL); | ||
1759 | del_timer_sync(&sbs->update_timer); | ||
1760 | |||
1761 | for (id = 0; id < MAX_SBS_BAT; id++) { | ||
1762 | acpi_battery_remove(sbs, id); | ||
1763 | } | ||
1764 | |||
1765 | acpi_ac_remove(sbs); | ||
1766 | |||
1767 | kfree(sbs); | ||
1768 | |||
1769 | return_VALUE(0); | ||
1770 | } | ||
1771 | |||
1772 | static int __init acpi_sbs_init(void) | ||
1773 | { | ||
1774 | int result = 0; | ||
1775 | |||
1776 | ACPI_FUNCTION_TRACE("acpi_sbs_init"); | ||
1777 | |||
1778 | init_MUTEX(&sbs_sem); | ||
1779 | |||
1780 | if (capacity_mode != DEF_CAPACITY_UNIT | ||
1781 | && capacity_mode != MAH_CAPACITY_UNIT | ||
1782 | && capacity_mode != MWH_CAPACITY_UNIT) { | ||
1783 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: " | ||
1784 | "invalid capacity_mode = %d\n", | ||
1785 | capacity_mode)); | ||
1786 | return_VALUE(-EINVAL); | ||
1787 | } | ||
1788 | |||
1789 | acpi_ac_dir = acpi_lock_ac_dir(); | ||
1790 | if (!acpi_ac_dir) { | ||
1791 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1792 | "acpi_lock_ac_dir() failed\n")); | ||
1793 | return_VALUE(-ENODEV); | ||
1794 | } | ||
1795 | |||
1796 | acpi_battery_dir = acpi_lock_battery_dir(); | ||
1797 | if (!acpi_battery_dir) { | ||
1798 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1799 | "acpi_lock_battery_dir() failed\n")); | ||
1800 | return_VALUE(-ENODEV); | ||
1801 | } | ||
1802 | |||
1803 | result = acpi_bus_register_driver(&acpi_sbs_driver); | ||
1804 | if (result < 0) { | ||
1805 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1806 | "acpi_bus_register_driver() failed\n")); | ||
1807 | return_VALUE(-ENODEV); | ||
1808 | } | ||
1809 | |||
1810 | return_VALUE(0); | ||
1811 | } | ||
1812 | |||
1813 | static void __exit acpi_sbs_exit(void) | ||
1814 | { | ||
1815 | ACPI_FUNCTION_TRACE("acpi_sbs_exit"); | ||
1816 | |||
1817 | acpi_bus_unregister_driver(&acpi_sbs_driver); | ||
1818 | |||
1819 | acpi_unlock_ac_dir(acpi_ac_dir); | ||
1820 | acpi_ac_dir = NULL; | ||
1821 | acpi_unlock_battery_dir(acpi_battery_dir); | ||
1822 | acpi_battery_dir = NULL; | ||
1823 | |||
1824 | return_VOID; | ||
1825 | } | ||
1826 | |||
1827 | module_init(acpi_sbs_init); | ||
1828 | module_exit(acpi_sbs_exit); | ||