blob: c1bae106833cef03d6f7faee83e5d42c9fda36a9 [file] [log] [blame]
Rich Townsend3f86b832006-07-01 11:36:54 -04001/*
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>
Vladimir Lebedev6d157022007-03-19 17:45:50 +030033#include <linux/timer.h>
Vladimir Lebedev72206232007-03-19 17:45:50 +030034#include <linux/jiffies.h>
Rich Townsend3f86b832006-07-01 11:36:54 -040035#include <linux/delay.h>
36
Rich Townsend3f86b832006-07-01 11:36:54 -040037#define ACPI_SBS_COMPONENT 0x00080000
38#define ACPI_SBS_CLASS "sbs"
39#define ACPI_AC_CLASS "ac_adapter"
40#define ACPI_BATTERY_CLASS "battery"
41#define ACPI_SBS_HID "ACPI0002"
Rich Townsend3f86b832006-07-01 11:36:54 -040042#define ACPI_SBS_DEVICE_NAME "Smart Battery System"
43#define ACPI_SBS_FILE_INFO "info"
44#define ACPI_SBS_FILE_STATE "state"
45#define ACPI_SBS_FILE_ALARM "alarm"
46#define ACPI_BATTERY_DIR_NAME "BAT%i"
47#define ACPI_AC_DIR_NAME "AC0"
48#define ACPI_SBC_SMBUS_ADDR 0x9
49#define ACPI_SBSM_SMBUS_ADDR 0xa
50#define ACPI_SB_SMBUS_ADDR 0xb
51#define ACPI_SBS_AC_NOTIFY_STATUS 0x80
52#define ACPI_SBS_BATTERY_NOTIFY_STATUS 0x80
53#define ACPI_SBS_BATTERY_NOTIFY_INFO 0x81
54
55#define _COMPONENT ACPI_SBS_COMPONENT
56
Len Brownf52fd662007-02-12 22:42:12 -050057ACPI_MODULE_NAME("sbs");
Rich Townsend3f86b832006-07-01 11:36:54 -040058
59MODULE_AUTHOR("Rich Townsend");
60MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
61MODULE_LICENSE("GPL");
62
Vladimir Lebedev6d157022007-03-19 17:45:50 +030063#define xmsleep(t) msleep(t)
64
65#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */
66
67#define ACPI_EC_SMB_STS 0x01 /* status */
68#define ACPI_EC_SMB_ADDR 0x02 /* address */
69#define ACPI_EC_SMB_CMD 0x03 /* command */
70#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */
71#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */
72
73#define ACPI_EC_SMB_STS_DONE 0x80
74#define ACPI_EC_SMB_STS_STATUS 0x1f
75
76#define ACPI_EC_SMB_PRTCL_WRITE 0x00
77#define ACPI_EC_SMB_PRTCL_READ 0x01
78#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08
79#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a
80
81#define ACPI_EC_SMB_TRANSACTION_SLEEP 1
82#define ACPI_EC_SMB_ACCESS_SLEEP1 1
83#define ACPI_EC_SMB_ACCESS_SLEEP2 10
84
85#define DEF_CAPACITY_UNIT 3
86#define MAH_CAPACITY_UNIT 1
87#define MWH_CAPACITY_UNIT 2
88#define CAPACITY_UNIT DEF_CAPACITY_UNIT
89
90#define REQUEST_UPDATE_MODE 1
91#define QUEUE_UPDATE_MODE 2
92
93#define DATA_TYPE_COMMON 0
94#define DATA_TYPE_INFO 1
95#define DATA_TYPE_STATE 2
96#define DATA_TYPE_ALARM 3
97#define DATA_TYPE_AC_STATE 4
98
99extern struct proc_dir_entry *acpi_lock_ac_dir(void);
100extern struct proc_dir_entry *acpi_lock_battery_dir(void);
101extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
102extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
103
104#define MAX_SBS_BAT 4
105#define ACPI_SBS_BLOCK_MAX 32
106
107#define ACPI_SBS_SMBUS_READ 1
108#define ACPI_SBS_SMBUS_WRITE 2
109
110#define ACPI_SBS_WORD_DATA 1
111#define ACPI_SBS_BLOCK_DATA 2
112
Vladimir Lebedev72206232007-03-19 17:45:50 +0300113#define UPDATE_DELAY 10
Rich Townsend3f86b832006-07-01 11:36:54 -0400114
Vladimir Lebedev72206232007-03-19 17:45:50 +0300115/* 0 - every time, > 0 - by update_time */
116static unsigned int update_time = 120;
Rich Townsend3f86b832006-07-01 11:36:54 -0400117
Vladimir Lebedev72206232007-03-19 17:45:50 +0300118static unsigned int capacity_mode = CAPACITY_UNIT;
Rich Townsend3f86b832006-07-01 11:36:54 -0400119
Vladimir Lebedev72206232007-03-19 17:45:50 +0300120module_param(update_time, uint, 0644);
121module_param(capacity_mode, uint, 0444);
Rich Townsend3f86b832006-07-01 11:36:54 -0400122
123static int acpi_sbs_add(struct acpi_device *device);
124static int acpi_sbs_remove(struct acpi_device *device, int type);
Vladimir Lebedev72206232007-03-19 17:45:50 +0300125static int acpi_sbs_resume(struct acpi_device *device);
Rich Townsend3f86b832006-07-01 11:36:54 -0400126
127static struct acpi_driver acpi_sbs_driver = {
Len Brownc2b6705b2007-02-12 23:33:40 -0500128 .name = "sbs",
Rich Townsend3f86b832006-07-01 11:36:54 -0400129 .class = ACPI_SBS_CLASS,
130 .ids = ACPI_SBS_HID,
131 .ops = {
132 .add = acpi_sbs_add,
133 .remove = acpi_sbs_remove,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300134 .resume = acpi_sbs_resume,
Rich Townsend3f86b832006-07-01 11:36:54 -0400135 },
136};
137
Vladimir Lebedev72206232007-03-19 17:45:50 +0300138struct acpi_ac {
139 int ac_present;
140};
141
Rich Townsend3f86b832006-07-01 11:36:54 -0400142struct acpi_battery_info {
143 int capacity_mode;
144 s16 full_charge_capacity;
145 s16 design_capacity;
146 s16 design_voltage;
147 int vscale;
148 int ipscale;
149 s16 serial_number;
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300150 char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3];
151 char device_name[ACPI_SBS_BLOCK_MAX + 3];
152 char device_chemistry[ACPI_SBS_BLOCK_MAX + 3];
Rich Townsend3f86b832006-07-01 11:36:54 -0400153};
154
155struct acpi_battery_state {
156 s16 voltage;
157 s16 amperage;
158 s16 remaining_capacity;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300159 s16 battery_state;
Rich Townsend3f86b832006-07-01 11:36:54 -0400160};
161
162struct acpi_battery_alarm {
163 s16 remaining_capacity;
164};
165
166struct acpi_battery {
167 int alive;
Rich Townsend3f86b832006-07-01 11:36:54 -0400168 int id;
169 int init_state;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300170 int battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -0400171 struct acpi_sbs *sbs;
172 struct acpi_battery_info info;
173 struct acpi_battery_state state;
174 struct acpi_battery_alarm alarm;
175 struct proc_dir_entry *battery_entry;
176};
177
178struct acpi_sbs {
179 acpi_handle handle;
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300180 int base;
Rich Townsend3f86b832006-07-01 11:36:54 -0400181 struct acpi_device *device;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300182 struct acpi_ec_smbus *smbus;
183 struct mutex mutex;
Rich Townsend3f86b832006-07-01 11:36:54 -0400184 int sbsm_present;
185 int sbsm_batteries_supported;
Rich Townsend3f86b832006-07-01 11:36:54 -0400186 struct proc_dir_entry *ac_entry;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300187 struct acpi_ac ac;
Rich Townsend3f86b832006-07-01 11:36:54 -0400188 struct acpi_battery battery[MAX_SBS_BAT];
Rich Townsend3f86b832006-07-01 11:36:54 -0400189 int zombie;
Rich Townsend3f86b832006-07-01 11:36:54 -0400190 struct timer_list update_timer;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300191 int run_cnt;
192 int update_proc_flg;
Rich Townsend3f86b832006-07-01 11:36:54 -0400193};
194
Vladimir Lebedev72206232007-03-19 17:45:50 +0300195static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
196static void acpi_sbs_update_time(void *data);
197
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300198union sbs_rw_data {
199 u16 word;
200 u8 block[ACPI_SBS_BLOCK_MAX + 2];
201};
202
203static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
204 char read_write, u8 command, int size,
205 union sbs_rw_data *data);
Rich Townsend3f86b832006-07-01 11:36:54 -0400206
207/* --------------------------------------------------------------------------
208 SMBus Communication
209 -------------------------------------------------------------------------- */
210
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300211static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data)
Rich Townsend3f86b832006-07-01 11:36:54 -0400212{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300213 u8 val;
214 int err;
Rich Townsend3f86b832006-07-01 11:36:54 -0400215
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300216 err = ec_read(sbs->base + address, &val);
217 if (!err) {
218 *data = val;
219 }
220 xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
221 return (err);
222}
Rich Townsend3f86b832006-07-01 11:36:54 -0400223
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300224static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data)
225{
226 int err;
Rich Townsend3f86b832006-07-01 11:36:54 -0400227
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300228 err = ec_write(sbs->base + address, data);
229 return (err);
230}
Rich Townsend3f86b832006-07-01 11:36:54 -0400231
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300232static int
233acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr,
234 char read_write, u8 command, int size,
235 union sbs_rw_data *data)
236{
237 unsigned char protocol, len = 0, temp[2] = { 0, 0 };
238 int i;
239
240 if (read_write == ACPI_SBS_SMBUS_READ) {
241 protocol = ACPI_EC_SMB_PRTCL_READ;
242 } else {
243 protocol = ACPI_EC_SMB_PRTCL_WRITE;
244 }
245
246 switch (size) {
247
248 case ACPI_SBS_WORD_DATA:
249 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
250 if (read_write == ACPI_SBS_SMBUS_WRITE) {
251 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word);
252 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1,
253 data->word >> 8);
254 }
255 protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA;
Rich Townsend3f86b832006-07-01 11:36:54 -0400256 break;
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300257 case ACPI_SBS_BLOCK_DATA:
258 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command);
259 if (read_write == ACPI_SBS_SMBUS_WRITE) {
260 len = min_t(u8, data->block[0], 32);
261 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len);
262 for (i = 0; i < len; i++)
263 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i,
264 data->block[i + 1]);
265 }
266 protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA;
Rich Townsend3f86b832006-07-01 11:36:54 -0400267 break;
268 default:
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300269 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev68451182007-03-19 17:45:50 +0300270 "unsupported transaction %d", size));
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300271 return (-1);
Rich Townsend3f86b832006-07-01 11:36:54 -0400272 }
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300273
274 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1);
275 acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol);
276
277 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
278
279 if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
280 xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1);
281 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
282 }
283 if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
284 xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
285 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp);
286 }
287 if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
288 || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
289 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev68451182007-03-19 17:45:50 +0300290 "transaction %d error", size));
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300291 return (-1);
292 }
293
294 if (read_write == ACPI_SBS_SMBUS_WRITE) {
295 return (0);
296 }
297
298 switch (size) {
299
300 case ACPI_SBS_WORD_DATA:
301 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp);
302 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1);
303 data->word = (temp[1] << 8) | temp[0];
304 break;
305
306 case ACPI_SBS_BLOCK_DATA:
307 len = 0;
308 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len);
309 len = min_t(u8, len, 32);
310 for (i = 0; i < len; i++)
311 acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i,
312 data->block + i + 1);
313 data->block[0] = len;
314 break;
315 default:
316 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev68451182007-03-19 17:45:50 +0300317 "unsupported transaction %d", size));
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300318 return (-1);
319 }
320
321 return (0);
Rich Townsend3f86b832006-07-01 11:36:54 -0400322}
323
324static int
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300325acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word)
Rich Townsend3f86b832006-07-01 11:36:54 -0400326{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300327 union sbs_rw_data data;
Rich Townsend3f86b832006-07-01 11:36:54 -0400328 int result = 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400329
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300330 result = acpi_ec_sbs_access(sbs, addr,
331 ACPI_SBS_SMBUS_READ, func,
332 ACPI_SBS_WORD_DATA, &data);
333 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300334 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
335 "acpi_ec_sbs_access() failed"));
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300336 } else {
337 *word = data.word;
Rich Townsend3f86b832006-07-01 11:36:54 -0400338 }
339
Len Brown635227e2006-07-01 16:48:23 -0400340 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400341}
342
343static int
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300344acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str)
Rich Townsend3f86b832006-07-01 11:36:54 -0400345{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300346 union sbs_rw_data data;
Rich Townsend3f86b832006-07-01 11:36:54 -0400347 int result = 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400348
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300349 result = acpi_ec_sbs_access(sbs, addr,
350 ACPI_SBS_SMBUS_READ, func,
351 ACPI_SBS_BLOCK_DATA, &data);
352 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300353 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
354 "acpi_ec_sbs_access() failed"));
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300355 } else {
356 strncpy(str, (const char *)data.block + 1, data.block[0]);
357 str[data.block[0]] = 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400358 }
359
Len Brown635227e2006-07-01 16:48:23 -0400360 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400361}
362
363static int
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300364acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word)
Rich Townsend3f86b832006-07-01 11:36:54 -0400365{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300366 union sbs_rw_data data;
Rich Townsend3f86b832006-07-01 11:36:54 -0400367 int result = 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400368
369 data.word = word;
370
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300371 result = acpi_ec_sbs_access(sbs, addr,
372 ACPI_SBS_SMBUS_WRITE, func,
373 ACPI_SBS_WORD_DATA, &data);
374 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300375 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
376 "acpi_ec_sbs_access() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400377 }
378
Len Brown635227e2006-07-01 16:48:23 -0400379 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400380}
381
Vladimir Lebedev72206232007-03-19 17:45:50 +0300382static int sbs_zombie(struct acpi_sbs *sbs)
383{
384 return (sbs->zombie);
385}
386
387static int sbs_mutex_lock(struct acpi_sbs *sbs)
388{
389 if (sbs_zombie(sbs)) {
390 return -ENODEV;
391 }
392 mutex_lock(&sbs->mutex);
393 return 0;
394}
395
396static void sbs_mutex_unlock(struct acpi_sbs *sbs)
397{
398 mutex_unlock(&sbs->mutex);
399}
400
Rich Townsend3f86b832006-07-01 11:36:54 -0400401/* --------------------------------------------------------------------------
402 Smart Battery System Management
403 -------------------------------------------------------------------------- */
404
Vladimir Lebedev72206232007-03-19 17:45:50 +0300405static int acpi_check_update_proc(struct acpi_sbs *sbs)
406{
407 acpi_status status = AE_OK;
408
409 if (update_time == 0) {
410 sbs->update_proc_flg = 0;
411 return 0;
412 }
413 if (sbs->update_proc_flg == 0) {
414 status = acpi_os_execute(OSL_GPE_HANDLER,
415 acpi_sbs_update_time, sbs);
416 if (status != AE_OK) {
417 ACPI_EXCEPTION((AE_INFO, status,
418 "acpi_os_execute() failed"));
419 return 1;
420 }
421 sbs->update_proc_flg = 1;
422 }
423 return 0;
424}
Rich Townsend3f86b832006-07-01 11:36:54 -0400425
426static int acpi_sbs_generate_event(struct acpi_device *device,
427 int event, int state, char *bid, char *class)
428{
429 char bid_saved[5];
430 char class_saved[20];
431 int result = 0;
432
Rich Townsend3f86b832006-07-01 11:36:54 -0400433 strcpy(bid_saved, acpi_device_bid(device));
434 strcpy(class_saved, acpi_device_class(device));
435
436 strcpy(acpi_device_bid(device), bid);
437 strcpy(acpi_device_class(device), class);
438
439 result = acpi_bus_generate_event(device, event, state);
440
441 strcpy(acpi_device_bid(device), bid_saved);
442 strcpy(acpi_device_class(device), class_saved);
443
Len Brown635227e2006-07-01 16:48:23 -0400444 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400445}
446
447static int acpi_battery_get_present(struct acpi_battery *battery)
448{
449 s16 state;
450 int result = 0;
451 int is_present = 0;
452
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300453 result = acpi_sbs_read_word(battery->sbs,
454 ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400455 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300456 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
457 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400458 }
459 if (!result) {
460 is_present = (state & 0x000f) & (1 << battery->id);
461 }
462 battery->battery_present = is_present;
463
Len Brown635227e2006-07-01 16:48:23 -0400464 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400465}
466
Rich Townsend3f86b832006-07-01 11:36:54 -0400467static int acpi_battery_select(struct acpi_battery *battery)
468{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300469 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400470 int result = 0;
471 s16 state;
472 int foo;
473
Vladimir Lebedev72206232007-03-19 17:45:50 +0300474 if (sbs->sbsm_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400475
476 /* Take special care not to knobble other nibbles of
477 * state (aka selector_state), since
478 * it causes charging to halt on SBSELs */
479
480 result =
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300481 acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400482 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300483 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
484 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400485 goto end;
486 }
487
488 foo = (state & 0x0fff) | (1 << (battery->id + 12));
489 result =
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300490 acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo);
Rich Townsend3f86b832006-07-01 11:36:54 -0400491 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300492 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
493 "acpi_sbs_write_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400494 goto end;
495 }
496 }
497
498 end:
Len Brown635227e2006-07-01 16:48:23 -0400499 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400500}
501
502static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
503{
Rich Townsend3f86b832006-07-01 11:36:54 -0400504 int result = 0;
505 s16 battery_system_info;
506
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300507 result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04,
508 &battery_system_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400509 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300510 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
511 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400512 goto end;
513 }
514
515 sbs->sbsm_batteries_supported = battery_system_info & 0x000f;
516
517 end:
518
Len Brown635227e2006-07-01 16:48:23 -0400519 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400520}
521
522static int acpi_battery_get_info(struct acpi_battery *battery)
523{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300524 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400525 int result = 0;
526 s16 battery_mode;
527 s16 specification_info;
528
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300529 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
530 &battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400531 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300532 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
533 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400534 goto end;
535 }
536 battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
537
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300538 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10,
539 &battery->info.full_charge_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400540 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300541 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
542 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400543 goto end;
544 }
545
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300546 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18,
547 &battery->info.design_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400548
549 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300550 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
551 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400552 goto end;
553 }
554
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300555 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19,
556 &battery->info.design_voltage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400557 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300558 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
559 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400560 goto end;
561 }
562
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300563 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a,
564 &specification_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400565 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300566 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
567 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400568 goto end;
569 }
570
571 switch ((specification_info & 0x0f00) >> 8) {
572 case 1:
573 battery->info.vscale = 10;
574 break;
575 case 2:
576 battery->info.vscale = 100;
577 break;
578 case 3:
579 battery->info.vscale = 1000;
580 break;
581 default:
582 battery->info.vscale = 1;
583 }
584
585 switch ((specification_info & 0xf000) >> 12) {
586 case 1:
587 battery->info.ipscale = 10;
588 break;
589 case 2:
590 battery->info.ipscale = 100;
591 break;
592 case 3:
593 battery->info.ipscale = 1000;
594 break;
595 default:
596 battery->info.ipscale = 1;
597 }
598
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300599 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c,
600 &battery->info.serial_number);
Rich Townsend3f86b832006-07-01 11:36:54 -0400601 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300602 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
603 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400604 goto end;
605 }
606
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300607 result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20,
608 battery->info.manufacturer_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400609 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300610 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
611 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400612 goto end;
613 }
614
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300615 result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21,
616 battery->info.device_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400617 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300618 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
619 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400620 goto end;
621 }
622
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300623 result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22,
624 battery->info.device_chemistry);
Rich Townsend3f86b832006-07-01 11:36:54 -0400625 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300626 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
627 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400628 goto end;
629 }
630
631 end:
Len Brown635227e2006-07-01 16:48:23 -0400632 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400633}
634
Rich Townsend3f86b832006-07-01 11:36:54 -0400635static int acpi_battery_get_state(struct acpi_battery *battery)
636{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300637 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400638 int result = 0;
639
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300640 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09,
641 &battery->state.voltage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400642 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300643 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
644 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400645 goto end;
646 }
647
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300648 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a,
649 &battery->state.amperage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400650 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300651 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
652 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400653 goto end;
654 }
655
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300656 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f,
657 &battery->state.remaining_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400658 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300659 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
660 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400661 goto end;
662 }
663
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300664 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300665 &battery->state.battery_state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400666 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300667 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
668 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400669 goto end;
670 }
671
Rich Townsend3f86b832006-07-01 11:36:54 -0400672 end:
Len Brown635227e2006-07-01 16:48:23 -0400673 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400674}
675
676static int acpi_battery_get_alarm(struct acpi_battery *battery)
677{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300678 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400679 int result = 0;
680
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300681 result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
682 &battery->alarm.remaining_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400683 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300684 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
685 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400686 goto end;
687 }
688
Rich Townsend3f86b832006-07-01 11:36:54 -0400689 end:
690
Len Brown635227e2006-07-01 16:48:23 -0400691 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400692}
693
694static int acpi_battery_set_alarm(struct acpi_battery *battery,
695 unsigned long alarm)
696{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300697 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400698 int result = 0;
699 s16 battery_mode;
700 int foo;
701
Rich Townsend3f86b832006-07-01 11:36:54 -0400702 result = acpi_battery_select(battery);
703 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300704 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
705 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400706 goto end;
707 }
708
709 /* If necessary, enable the alarm */
710
711 if (alarm > 0) {
712 result =
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300713 acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03,
714 &battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400715 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300716 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
717 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400718 goto end;
719 }
720
721 result =
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300722 acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01,
723 battery_mode & 0xbfff);
Rich Townsend3f86b832006-07-01 11:36:54 -0400724 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300725 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
726 "acpi_sbs_write_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400727 goto end;
728 }
729 }
730
731 foo = alarm / (battery->info.capacity_mode ? 10 : 1);
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300732 result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo);
Rich Townsend3f86b832006-07-01 11:36:54 -0400733 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300734 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
735 "acpi_sbs_write_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400736 goto end;
737 }
738
739 end:
740
Len Brown635227e2006-07-01 16:48:23 -0400741 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400742}
743
744static int acpi_battery_set_mode(struct acpi_battery *battery)
745{
Vladimir Lebedev72206232007-03-19 17:45:50 +0300746 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400747 int result = 0;
748 s16 battery_mode;
749
Rich Townsend3f86b832006-07-01 11:36:54 -0400750 if (capacity_mode == DEF_CAPACITY_UNIT) {
751 goto end;
752 }
753
Vladimir Lebedev72206232007-03-19 17:45:50 +0300754 result = acpi_sbs_read_word(sbs,
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300755 ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400756 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300757 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
758 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400759 goto end;
760 }
761
762 if (capacity_mode == MAH_CAPACITY_UNIT) {
763 battery_mode &= 0x7fff;
764 } else {
765 battery_mode |= 0x8000;
766 }
Vladimir Lebedev72206232007-03-19 17:45:50 +0300767 result = acpi_sbs_write_word(sbs,
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300768 ACPI_SB_SMBUS_ADDR, 0x03, battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400769 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300770 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
771 "acpi_sbs_write_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400772 goto end;
773 }
774
Vladimir Lebedev72206232007-03-19 17:45:50 +0300775 result = acpi_sbs_read_word(sbs,
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300776 ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400777 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300778 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
779 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400780 goto end;
781 }
782
783 end:
Len Brown635227e2006-07-01 16:48:23 -0400784 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400785}
786
787static int acpi_battery_init(struct acpi_battery *battery)
788{
789 int result = 0;
790
Rich Townsend3f86b832006-07-01 11:36:54 -0400791 result = acpi_battery_select(battery);
792 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300793 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300794 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400795 goto end;
796 }
797
798 result = acpi_battery_set_mode(battery);
799 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300800 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
801 "acpi_battery_set_mode() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400802 goto end;
803 }
804
805 result = acpi_battery_get_info(battery);
806 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300807 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
808 "acpi_battery_get_info() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400809 goto end;
810 }
811
812 result = acpi_battery_get_state(battery);
813 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300814 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
815 "acpi_battery_get_state() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400816 goto end;
817 }
818
819 result = acpi_battery_get_alarm(battery);
820 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300821 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
822 "acpi_battery_get_alarm() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400823 goto end;
824 }
825
826 end:
Len Brown635227e2006-07-01 16:48:23 -0400827 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400828}
829
830static int acpi_ac_get_present(struct acpi_sbs *sbs)
831{
Rich Townsend3f86b832006-07-01 11:36:54 -0400832 int result = 0;
833 s16 charger_status;
834
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300835 result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13,
836 &charger_status);
Rich Townsend3f86b832006-07-01 11:36:54 -0400837
838 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300839 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
840 "acpi_sbs_read_word() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400841 goto end;
842 }
843
Vladimir Lebedev72206232007-03-19 17:45:50 +0300844 sbs->ac.ac_present = (charger_status & 0x8000) >> 15;
Rich Townsend3f86b832006-07-01 11:36:54 -0400845
846 end:
847
Len Brown635227e2006-07-01 16:48:23 -0400848 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400849}
850
851/* --------------------------------------------------------------------------
852 FS Interface (/proc/acpi)
853 -------------------------------------------------------------------------- */
854
855/* Generic Routines */
856
857static int
858acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
859 struct proc_dir_entry *parent_dir,
860 char *dir_name,
861 struct file_operations *info_fops,
862 struct file_operations *state_fops,
863 struct file_operations *alarm_fops, void *data)
864{
865 struct proc_dir_entry *entry = NULL;
866
Rich Townsend3f86b832006-07-01 11:36:54 -0400867 if (!*dir) {
868 *dir = proc_mkdir(dir_name, parent_dir);
869 if (!*dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300870 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
871 "proc_mkdir() failed"));
Len Brown635227e2006-07-01 16:48:23 -0400872 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400873 }
874 (*dir)->owner = THIS_MODULE;
875 }
876
877 /* 'info' [R] */
878 if (info_fops) {
879 entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
880 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300881 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
882 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400883 } else {
884 entry->proc_fops = info_fops;
885 entry->data = data;
886 entry->owner = THIS_MODULE;
887 }
888 }
889
890 /* 'state' [R] */
891 if (state_fops) {
892 entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
893 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300894 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
895 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400896 } else {
897 entry->proc_fops = state_fops;
898 entry->data = data;
899 entry->owner = THIS_MODULE;
900 }
901 }
902
903 /* 'alarm' [R/W] */
904 if (alarm_fops) {
905 entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
906 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300907 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
908 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400909 } else {
910 entry->proc_fops = alarm_fops;
911 entry->data = data;
912 entry->owner = THIS_MODULE;
913 }
914 }
915
Len Brown635227e2006-07-01 16:48:23 -0400916 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400917}
918
919static void
920acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir,
921 struct proc_dir_entry *parent_dir)
922{
Rich Townsend3f86b832006-07-01 11:36:54 -0400923
924 if (*dir) {
925 remove_proc_entry(ACPI_SBS_FILE_INFO, *dir);
926 remove_proc_entry(ACPI_SBS_FILE_STATE, *dir);
927 remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir);
928 remove_proc_entry((*dir)->name, parent_dir);
929 *dir = NULL;
930 }
931
932}
933
934/* Smart Battery Interface */
935
936static struct proc_dir_entry *acpi_battery_dir = NULL;
937
938static int acpi_battery_read_info(struct seq_file *seq, void *offset)
939{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200940 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300941 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400942 int cscale;
943 int result = 0;
944
Vladimir Lebedev72206232007-03-19 17:45:50 +0300945 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400946 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400947 }
948
Vladimir Lebedev72206232007-03-19 17:45:50 +0300949 result = acpi_check_update_proc(sbs);
950 if (result)
951 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400952
Vladimir Lebedev72206232007-03-19 17:45:50 +0300953 if (update_time == 0) {
954 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
Rich Townsend3f86b832006-07-01 11:36:54 -0400955 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300956 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
957 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400958 }
959 }
960
Vladimir Lebedev72206232007-03-19 17:45:50 +0300961 if (battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400962 seq_printf(seq, "present: yes\n");
963 } else {
964 seq_printf(seq, "present: no\n");
965 goto end;
966 }
967
968 if (battery->info.capacity_mode) {
969 cscale = battery->info.vscale * battery->info.ipscale;
970 } else {
971 cscale = battery->info.ipscale;
972 }
Vladimir Lebedev72206232007-03-19 17:45:50 +0300973 seq_printf(seq, "design capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400974 battery->info.design_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300975 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400976
Vladimir Lebedev72206232007-03-19 17:45:50 +0300977 seq_printf(seq, "last full capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400978 battery->info.full_charge_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300979 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400980
981 seq_printf(seq, "battery technology: rechargeable\n");
982
983 seq_printf(seq, "design voltage: %i mV\n",
984 battery->info.design_voltage * battery->info.vscale);
985
986 seq_printf(seq, "design capacity warning: unknown\n");
987 seq_printf(seq, "design capacity low: unknown\n");
988 seq_printf(seq, "capacity granularity 1: unknown\n");
989 seq_printf(seq, "capacity granularity 2: unknown\n");
990
991 seq_printf(seq, "model number: %s\n",
992 battery->info.device_name);
993
994 seq_printf(seq, "serial number: %i\n",
995 battery->info.serial_number);
996
997 seq_printf(seq, "battery type: %s\n",
998 battery->info.device_chemistry);
999
1000 seq_printf(seq, "OEM info: %s\n",
1001 battery->info.manufacturer_name);
1002
1003 end:
1004
Vladimir Lebedev72206232007-03-19 17:45:50 +03001005 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001006
Len Brown635227e2006-07-01 16:48:23 -04001007 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001008}
1009
1010static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
1011{
1012 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
1013}
1014
1015static int acpi_battery_read_state(struct seq_file *seq, void *offset)
1016{
Vladimir Lebedev72206232007-03-19 17:45:50 +03001017 struct acpi_battery *battery = seq->private;
1018 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001019 int result = 0;
1020 int cscale;
1021 int foo;
1022
Vladimir Lebedev72206232007-03-19 17:45:50 +03001023 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001024 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001025 }
1026
Vladimir Lebedev72206232007-03-19 17:45:50 +03001027 result = acpi_check_update_proc(sbs);
1028 if (result)
1029 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001030
Vladimir Lebedev72206232007-03-19 17:45:50 +03001031 if (update_time == 0) {
1032 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -04001033 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001034 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1035 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001036 }
1037 }
1038
Vladimir Lebedev72206232007-03-19 17:45:50 +03001039 if (battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001040 seq_printf(seq, "present: yes\n");
1041 } else {
1042 seq_printf(seq, "present: no\n");
1043 goto end;
1044 }
1045
1046 if (battery->info.capacity_mode) {
1047 cscale = battery->info.vscale * battery->info.ipscale;
1048 } else {
1049 cscale = battery->info.ipscale;
1050 }
1051
Vladimir Lebedev72206232007-03-19 17:45:50 +03001052 if (battery->state.battery_state & 0x0010) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001053 seq_printf(seq, "capacity state: critical\n");
1054 } else {
1055 seq_printf(seq, "capacity state: ok\n");
1056 }
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -05001057
1058 foo = (s16) battery->state.amperage * battery->info.ipscale;
1059 if (battery->info.capacity_mode) {
1060 foo = foo * battery->info.design_voltage / 1000;
1061 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001062 if (battery->state.amperage < 0) {
1063 seq_printf(seq, "charging state: discharging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -05001064 seq_printf(seq, "present rate: %d %s\n",
1065 -foo, battery->info.capacity_mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -04001066 } else if (battery->state.amperage > 0) {
1067 seq_printf(seq, "charging state: charging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -05001068 seq_printf(seq, "present rate: %d %s\n",
1069 foo, battery->info.capacity_mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -04001070 } else {
1071 seq_printf(seq, "charging state: charged\n");
1072 seq_printf(seq, "present rate: 0 %s\n",
1073 battery->info.capacity_mode ? "mW" : "mA");
1074 }
1075
Vladimir Lebedev72206232007-03-19 17:45:50 +03001076 seq_printf(seq, "remaining capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -04001077 battery->state.remaining_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001078 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -04001079
1080 seq_printf(seq, "present voltage: %i mV\n",
1081 battery->state.voltage * battery->info.vscale);
1082
1083 end:
1084
Vladimir Lebedev72206232007-03-19 17:45:50 +03001085 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001086
Len Brown635227e2006-07-01 16:48:23 -04001087 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001088}
1089
1090static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
1091{
1092 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
1093}
1094
1095static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
1096{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001097 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001098 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001099 int result = 0;
1100 int cscale;
1101
Vladimir Lebedev72206232007-03-19 17:45:50 +03001102 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001103 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001104 }
1105
Vladimir Lebedev72206232007-03-19 17:45:50 +03001106 result = acpi_check_update_proc(sbs);
1107 if (result)
1108 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001109
Vladimir Lebedev72206232007-03-19 17:45:50 +03001110 if (update_time == 0) {
1111 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
Rich Townsend3f86b832006-07-01 11:36:54 -04001112 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001113 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1114 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001115 }
1116 }
1117
Vladimir Lebedev72206232007-03-19 17:45:50 +03001118 if (!battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001119 seq_printf(seq, "present: no\n");
1120 goto end;
1121 }
1122
1123 if (battery->info.capacity_mode) {
1124 cscale = battery->info.vscale * battery->info.ipscale;
1125 } else {
1126 cscale = battery->info.ipscale;
1127 }
1128
1129 seq_printf(seq, "alarm: ");
1130 if (battery->alarm.remaining_capacity) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001131 seq_printf(seq, "%i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -04001132 battery->alarm.remaining_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001133 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -04001134 } else {
1135 seq_printf(seq, "disabled\n");
1136 }
1137
1138 end:
1139
Vladimir Lebedev72206232007-03-19 17:45:50 +03001140 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001141
Len Brown635227e2006-07-01 16:48:23 -04001142 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001143}
1144
1145static ssize_t
1146acpi_battery_write_alarm(struct file *file, const char __user * buffer,
1147 size_t count, loff_t * ppos)
1148{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001149 struct seq_file *seq = file->private_data;
1150 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001151 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001152 char alarm_string[12] = { '\0' };
1153 int result, old_alarm, new_alarm;
1154
Vladimir Lebedev72206232007-03-19 17:45:50 +03001155 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001156 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001157 }
1158
Vladimir Lebedev72206232007-03-19 17:45:50 +03001159 result = acpi_check_update_proc(sbs);
1160 if (result)
1161 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001162
Vladimir Lebedev72206232007-03-19 17:45:50 +03001163 if (!battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001164 result = -ENODEV;
1165 goto end;
1166 }
1167
1168 if (count > sizeof(alarm_string) - 1) {
1169 result = -EINVAL;
1170 goto end;
1171 }
1172
1173 if (copy_from_user(alarm_string, buffer, count)) {
1174 result = -EFAULT;
1175 goto end;
1176 }
1177
1178 alarm_string[count] = 0;
1179
1180 old_alarm = battery->alarm.remaining_capacity;
1181 new_alarm = simple_strtoul(alarm_string, NULL, 0);
1182
1183 result = acpi_battery_set_alarm(battery, new_alarm);
1184 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001185 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1186 "acpi_battery_set_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001187 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -04001188 goto end;
1189 }
1190 result = acpi_battery_get_alarm(battery);
1191 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001192 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1193 "acpi_battery_get_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001194 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -04001195 goto end;
1196 }
1197
1198 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001199 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001200
1201 if (result) {
Len Brown635227e2006-07-01 16:48:23 -04001202 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001203 } else {
Len Brown635227e2006-07-01 16:48:23 -04001204 return count;
Rich Townsend3f86b832006-07-01 11:36:54 -04001205 }
1206}
1207
1208static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
1209{
1210 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
1211}
1212
1213static struct file_operations acpi_battery_info_fops = {
1214 .open = acpi_battery_info_open_fs,
1215 .read = seq_read,
1216 .llseek = seq_lseek,
1217 .release = single_release,
1218 .owner = THIS_MODULE,
1219};
1220
1221static struct file_operations acpi_battery_state_fops = {
1222 .open = acpi_battery_state_open_fs,
1223 .read = seq_read,
1224 .llseek = seq_lseek,
1225 .release = single_release,
1226 .owner = THIS_MODULE,
1227};
1228
1229static struct file_operations acpi_battery_alarm_fops = {
1230 .open = acpi_battery_alarm_open_fs,
1231 .read = seq_read,
1232 .write = acpi_battery_write_alarm,
1233 .llseek = seq_lseek,
1234 .release = single_release,
1235 .owner = THIS_MODULE,
1236};
1237
1238/* Legacy AC Adapter Interface */
1239
1240static struct proc_dir_entry *acpi_ac_dir = NULL;
1241
1242static int acpi_ac_read_state(struct seq_file *seq, void *offset)
1243{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001244 struct acpi_sbs *sbs = seq->private;
Rich Townsend3f86b832006-07-01 11:36:54 -04001245 int result;
1246
Vladimir Lebedev72206232007-03-19 17:45:50 +03001247 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001248 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001249 }
1250
Vladimir Lebedev72206232007-03-19 17:45:50 +03001251 if (update_time == 0) {
1252 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -04001253 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001254 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1255 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001256 }
1257 }
1258
1259 seq_printf(seq, "state: %s\n",
Vladimir Lebedev72206232007-03-19 17:45:50 +03001260 sbs->ac.ac_present ? "on-line" : "off-line");
Rich Townsend3f86b832006-07-01 11:36:54 -04001261
Vladimir Lebedev72206232007-03-19 17:45:50 +03001262 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001263
Len Brown635227e2006-07-01 16:48:23 -04001264 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001265}
1266
1267static int acpi_ac_state_open_fs(struct inode *inode, struct file *file)
1268{
1269 return single_open(file, acpi_ac_read_state, PDE(inode)->data);
1270}
1271
1272static struct file_operations acpi_ac_state_fops = {
1273 .open = acpi_ac_state_open_fs,
1274 .read = seq_read,
1275 .llseek = seq_lseek,
1276 .release = single_release,
1277 .owner = THIS_MODULE,
1278};
1279
1280/* --------------------------------------------------------------------------
1281 Driver Interface
1282 -------------------------------------------------------------------------- */
1283
1284/* Smart Battery */
1285
1286static int acpi_battery_add(struct acpi_sbs *sbs, int id)
1287{
1288 int is_present;
1289 int result;
1290 char dir_name[32];
1291 struct acpi_battery *battery;
1292
Rich Townsend3f86b832006-07-01 11:36:54 -04001293 battery = &sbs->battery[id];
1294
1295 battery->alive = 0;
1296
1297 battery->init_state = 0;
1298 battery->id = id;
1299 battery->sbs = sbs;
1300
1301 result = acpi_battery_select(battery);
1302 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001303 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1304 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001305 goto end;
1306 }
1307
1308 result = acpi_battery_get_present(battery);
1309 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001310 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1311 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001312 goto end;
1313 }
1314
Vladimir Lebedev72206232007-03-19 17:45:50 +03001315 is_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001316
1317 if (is_present) {
1318 result = acpi_battery_init(battery);
1319 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001320 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1321 "acpi_battery_init() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001322 goto end;
1323 }
1324 battery->init_state = 1;
1325 }
1326
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001327 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Rich Townsend3f86b832006-07-01 11:36:54 -04001328
1329 result = acpi_sbs_generic_add_fs(&battery->battery_entry,
1330 acpi_battery_dir,
1331 dir_name,
1332 &acpi_battery_info_fops,
1333 &acpi_battery_state_fops,
1334 &acpi_battery_alarm_fops, battery);
1335 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001336 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1337 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001338 goto end;
1339 }
1340 battery->alive = 1;
1341
Vladimir Lebedev72206232007-03-19 17:45:50 +03001342 printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
1343 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
1344 sbs->battery->battery_present ? "present" : "absent");
1345
Rich Townsend3f86b832006-07-01 11:36:54 -04001346 end:
Len Brown635227e2006-07-01 16:48:23 -04001347 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001348}
1349
1350static void acpi_battery_remove(struct acpi_sbs *sbs, int id)
1351{
Rich Townsend3f86b832006-07-01 11:36:54 -04001352
1353 if (sbs->battery[id].battery_entry) {
1354 acpi_sbs_generic_remove_fs(&(sbs->battery[id].battery_entry),
1355 acpi_battery_dir);
1356 }
1357}
1358
1359static int acpi_ac_add(struct acpi_sbs *sbs)
1360{
1361 int result;
1362
Rich Townsend3f86b832006-07-01 11:36:54 -04001363 result = acpi_ac_get_present(sbs);
1364 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001365 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1366 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001367 goto end;
1368 }
1369
1370 result = acpi_sbs_generic_add_fs(&sbs->ac_entry,
1371 acpi_ac_dir,
1372 ACPI_AC_DIR_NAME,
1373 NULL, &acpi_ac_state_fops, NULL, sbs);
1374 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001375 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1376 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001377 goto end;
1378 }
1379
Vladimir Lebedev72206232007-03-19 17:45:50 +03001380 printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
1381 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
1382 ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line");
1383
Rich Townsend3f86b832006-07-01 11:36:54 -04001384 end:
1385
Len Brown635227e2006-07-01 16:48:23 -04001386 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001387}
1388
1389static void acpi_ac_remove(struct acpi_sbs *sbs)
1390{
Rich Townsend3f86b832006-07-01 11:36:54 -04001391
1392 if (sbs->ac_entry) {
1393 acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir);
1394 }
1395}
1396
Vladimir Lebedev72206232007-03-19 17:45:50 +03001397static void acpi_sbs_update_time_run(unsigned long data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001398{
Vladimir Lebedev72206232007-03-19 17:45:50 +03001399 acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
Rich Townsend3f86b832006-07-01 11:36:54 -04001400}
1401
Vladimir Lebedev72206232007-03-19 17:45:50 +03001402static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001403{
1404 struct acpi_battery *battery;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001405 int result = 0, cnt;
1406 int old_ac_present = -1;
1407 int old_battery_present = -1;
1408 int new_ac_present = -1;
1409 int new_battery_present = -1;
1410 int id_min = 0, id_max = MAX_SBS_BAT - 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001411 char dir_name[32];
Vladimir Lebedev72206232007-03-19 17:45:50 +03001412 int do_battery_init = 0, do_ac_init = 0;
1413 int old_remaining_capacity = 0;
1414 int update_ac = 1, update_battery = 1;
1415 int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001416
Vladimir Lebedev72206232007-03-19 17:45:50 +03001417 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001418 goto end;
1419 }
1420
Vladimir Lebedev72206232007-03-19 17:45:50 +03001421 if (id >= 0) {
1422 id_min = id_max = id;
1423 }
1424
1425 if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
1426 cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1427 if (sbs->run_cnt % cnt != 0) {
1428 update_battery = 0;
1429 }
1430 }
1431
1432 sbs->run_cnt++;
1433
1434 if (!update_ac && !update_battery) {
1435 goto end;
1436 }
1437
1438 old_ac_present = sbs->ac.ac_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001439
1440 result = acpi_ac_get_present(sbs);
1441 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001442 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1443 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001444 }
1445
Vladimir Lebedev72206232007-03-19 17:45:50 +03001446 new_ac_present = sbs->ac.ac_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001447
1448 do_ac_init = (old_ac_present != new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001449 if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
1450 do_ac_init = 1;
1451 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001452
Vladimir Lebedev72206232007-03-19 17:45:50 +03001453 if (do_ac_init) {
1454 result = acpi_sbs_generate_event(sbs->device,
1455 ACPI_SBS_AC_NOTIFY_STATUS,
1456 new_ac_present,
1457 ACPI_AC_DIR_NAME,
1458 ACPI_AC_CLASS);
1459 if (result) {
1460 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1461 "acpi_sbs_generate_event() failed"));
1462 }
1463 }
1464
1465 if (data_type == DATA_TYPE_COMMON) {
1466 if (!do_ac_init && !update_battery) {
1467 goto end;
1468 }
1469 }
1470
1471 if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001472 goto end;
1473 }
1474
Vladimir Lebedev72206232007-03-19 17:45:50 +03001475 for (id = id_min; id <= id_max; id++) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001476 battery = &sbs->battery[id];
1477 if (battery->alive == 0) {
1478 continue;
1479 }
1480
1481 old_remaining_capacity = battery->state.remaining_capacity;
1482
Vladimir Lebedev72206232007-03-19 17:45:50 +03001483 old_battery_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001484
1485 result = acpi_battery_select(battery);
1486 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001487 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1488 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001489 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001490
1491 result = acpi_battery_get_present(battery);
1492 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001493 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1494 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001495 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001496
Vladimir Lebedev72206232007-03-19 17:45:50 +03001497 new_battery_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001498
1499 do_battery_init = ((old_battery_present != new_battery_present)
1500 && new_battery_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001501 if (!new_battery_present)
1502 goto event;
1503 if (do_ac_init || do_battery_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001504 result = acpi_battery_init(battery);
1505 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001506 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1507 "acpi_battery_init() "
1508 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001509 }
1510 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001511 if (sbs_zombie(sbs)) {
1512 goto end;
1513 }
1514
1515 if ((data_type == DATA_TYPE_COMMON
1516 || data_type == DATA_TYPE_INFO)
1517 && new_battery_present) {
1518 result = acpi_battery_get_info(battery);
1519 if (result) {
1520 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1521 "acpi_battery_get_info() failed"));
1522 }
1523 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001524 if (data_type == DATA_TYPE_INFO) {
1525 continue;
1526 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001527 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001528 goto end;
1529 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001530
1531 if ((data_type == DATA_TYPE_COMMON
1532 || data_type == DATA_TYPE_STATE)
1533 && new_battery_present) {
1534 result = acpi_battery_get_state(battery);
1535 if (result) {
1536 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1537 "acpi_battery_get_state() failed"));
1538 }
1539 }
1540 if (data_type == DATA_TYPE_STATE) {
1541 goto event;
1542 }
1543 if (sbs_zombie(sbs)) {
1544 goto end;
1545 }
1546
1547 if ((data_type == DATA_TYPE_COMMON
1548 || data_type == DATA_TYPE_ALARM)
1549 && new_battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001550 result = acpi_battery_get_alarm(battery);
1551 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001552 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1553 "acpi_battery_get_alarm() "
1554 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001555 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001556 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001557 if (data_type == DATA_TYPE_ALARM) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001558 continue;
1559 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001560 if (sbs_zombie(sbs)) {
1561 goto end;
1562 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001563
Vladimir Lebedev72206232007-03-19 17:45:50 +03001564 event:
1565
1566 if (old_battery_present != new_battery_present || do_ac_init ||
1567 old_remaining_capacity !=
1568 battery->state.remaining_capacity) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001569 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Rich Townsend3f86b832006-07-01 11:36:54 -04001570 result = acpi_sbs_generate_event(sbs->device,
1571 ACPI_SBS_BATTERY_NOTIFY_STATUS,
1572 new_battery_present,
1573 dir_name,
1574 ACPI_BATTERY_CLASS);
1575 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001576 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1577 "acpi_sbs_generate_event() "
1578 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001579 }
1580 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001581 }
1582
1583 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001584
Len Brown635227e2006-07-01 16:48:23 -04001585 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001586}
1587
Vladimir Lebedev72206232007-03-19 17:45:50 +03001588static void acpi_sbs_update_time(void *data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001589{
1590 struct acpi_sbs *sbs = data;
1591 unsigned long delay = -1;
1592 int result;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001593 unsigned int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001594
Vladimir Lebedev72206232007-03-19 17:45:50 +03001595 if (sbs_mutex_lock(sbs))
1596 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001597
Vladimir Lebedev72206232007-03-19 17:45:50 +03001598 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
Rich Townsend3f86b832006-07-01 11:36:54 -04001599 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001600 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1601 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001602 }
1603
Vladimir Lebedev72206232007-03-19 17:45:50 +03001604 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001605 goto end;
1606 }
1607
Vladimir Lebedev72206232007-03-19 17:45:50 +03001608 if (!up_tm) {
1609 if (timer_pending(&sbs->update_timer))
1610 del_timer(&sbs->update_timer);
1611 } else {
1612 delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1613 delay = jiffies + HZ * delay;
1614 if (timer_pending(&sbs->update_timer)) {
1615 mod_timer(&sbs->update_timer, delay);
1616 } else {
1617 sbs->update_timer.data = (unsigned long)data;
1618 sbs->update_timer.function = acpi_sbs_update_time_run;
1619 sbs->update_timer.expires = delay;
1620 add_timer(&sbs->update_timer);
1621 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001622 }
1623
Rich Townsend3f86b832006-07-01 11:36:54 -04001624 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001625
1626 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001627}
1628
1629static int acpi_sbs_add(struct acpi_device *device)
1630{
1631 struct acpi_sbs *sbs = NULL;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001632 int result = 0, remove_result = 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001633 unsigned long sbs_obj;
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001634 int id;
Rich Townsend3f86b832006-07-01 11:36:54 -04001635 acpi_status status = AE_OK;
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001636 unsigned long val;
1637
1638 status =
1639 acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val);
1640 if (ACPI_FAILURE(status)) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001641 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC"));
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001642 return -EIO;
1643 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001644
Burman Yan36bcbec2006-12-19 12:56:11 -08001645 sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
Rich Townsend3f86b832006-07-01 11:36:54 -04001646 if (!sbs) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001647 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
1648 result = -ENOMEM;
1649 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001650 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001651
Vladimir Lebedev72206232007-03-19 17:45:50 +03001652 mutex_init(&sbs->mutex);
1653
1654 sbs_mutex_lock(sbs);
1655
1656 sbs->base = (val & 0xff00ull) >> 8;
Rich Townsend3f86b832006-07-01 11:36:54 -04001657 sbs->device = device;
Rich Townsend3f86b832006-07-01 11:36:54 -04001658
1659 strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
1660 strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
1661 acpi_driver_data(device) = sbs;
1662
Rich Townsend3f86b832006-07-01 11:36:54 -04001663 result = acpi_ac_add(sbs);
1664 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001665 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001666 goto end;
1667 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001668 status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj);
1669 if (status) {
1670 ACPI_EXCEPTION((AE_INFO, status,
Vladimir Lebedev68451182007-03-19 17:45:50 +03001671 "acpi_evaluate_integer() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001672 result = -EIO;
1673 goto end;
1674 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001675 if (sbs_obj > 0) {
1676 result = acpi_sbsm_get_info(sbs);
1677 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001678 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1679 "acpi_sbsm_get_info() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001680 goto end;
1681 }
1682 sbs->sbsm_present = 1;
1683 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001684
Rich Townsend3f86b832006-07-01 11:36:54 -04001685 if (sbs->sbsm_present == 0) {
1686 result = acpi_battery_add(sbs, 0);
1687 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001688 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1689 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001690 goto end;
1691 }
1692 } else {
1693 for (id = 0; id < MAX_SBS_BAT; id++) {
1694 if ((sbs->sbsm_batteries_supported & (1 << id))) {
1695 result = acpi_battery_add(sbs, id);
1696 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001697 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001698 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001699 goto end;
1700 }
1701 }
1702 }
1703 }
1704
1705 sbs->handle = device->handle;
1706
1707 init_timer(&sbs->update_timer);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001708 result = acpi_check_update_proc(sbs);
1709 if (result)
1710 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001711
1712 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001713
1714 sbs_mutex_unlock(sbs);
1715
Rich Townsend3f86b832006-07-01 11:36:54 -04001716 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001717 remove_result = acpi_sbs_remove(device, 0);
1718 if (remove_result) {
1719 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1720 "acpi_sbs_remove() failed"));
1721 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001722 }
1723
Len Brown635227e2006-07-01 16:48:23 -04001724 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001725}
1726
Vladimir Lebedev72206232007-03-19 17:45:50 +03001727static int acpi_sbs_remove(struct acpi_device *device, int type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001728{
Len Browncece9012006-12-16 01:04:27 -05001729 struct acpi_sbs *sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001730 int id;
1731
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001732 if (!device) {
1733 return -EINVAL;
1734 }
1735
Vladimir Lebedev72206232007-03-19 17:45:50 +03001736 sbs = acpi_driver_data(device);
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001737 if (!sbs) {
Len Brown635227e2006-07-01 16:48:23 -04001738 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001739 }
1740
Vladimir Lebedev72206232007-03-19 17:45:50 +03001741 sbs_mutex_lock(sbs);
1742
Rich Townsend3f86b832006-07-01 11:36:54 -04001743 sbs->zombie = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001744 del_timer_sync(&sbs->update_timer);
1745 acpi_os_wait_events_complete(NULL);
1746 del_timer_sync(&sbs->update_timer);
1747
1748 for (id = 0; id < MAX_SBS_BAT; id++) {
1749 acpi_battery_remove(sbs, id);
1750 }
1751
1752 acpi_ac_remove(sbs);
1753
Vladimir Lebedev72206232007-03-19 17:45:50 +03001754 sbs_mutex_unlock(sbs);
1755
1756 mutex_destroy(&sbs->mutex);
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001757
Rich Townsend3f86b832006-07-01 11:36:54 -04001758 kfree(sbs);
1759
Len Brown635227e2006-07-01 16:48:23 -04001760 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001761}
1762
Vladimir Lebedev72206232007-03-19 17:45:50 +03001763static void acpi_sbs_rmdirs(void)
1764{
1765 if (acpi_ac_dir) {
1766 acpi_unlock_ac_dir(acpi_ac_dir);
1767 acpi_ac_dir = NULL;
1768 }
1769 if (acpi_battery_dir) {
1770 acpi_unlock_battery_dir(acpi_battery_dir);
1771 acpi_battery_dir = NULL;
1772 }
1773}
1774
1775static int acpi_sbs_resume(struct acpi_device *device)
1776{
1777 struct acpi_sbs *sbs;
1778
1779 if (!device)
1780 return -EINVAL;
1781
1782 sbs = device->driver_data;
1783
1784 sbs->run_cnt = 0;
1785
1786 return 0;
1787}
1788
Rich Townsend3f86b832006-07-01 11:36:54 -04001789static int __init acpi_sbs_init(void)
1790{
1791 int result = 0;
1792
Len Brownb20d2ae2006-08-15 23:21:37 -04001793 if (acpi_disabled)
1794 return -ENODEV;
1795
Rich Townsend3f86b832006-07-01 11:36:54 -04001796 if (capacity_mode != DEF_CAPACITY_UNIT
1797 && capacity_mode != MAH_CAPACITY_UNIT
1798 && capacity_mode != MWH_CAPACITY_UNIT) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001799 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev68451182007-03-19 17:45:50 +03001800 "invalid capacity_mode = %d", capacity_mode));
Len Brown635227e2006-07-01 16:48:23 -04001801 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001802 }
1803
1804 acpi_ac_dir = acpi_lock_ac_dir();
1805 if (!acpi_ac_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001806 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1807 "acpi_lock_ac_dir() failed"));
Len Brown635227e2006-07-01 16:48:23 -04001808 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001809 }
1810
1811 acpi_battery_dir = acpi_lock_battery_dir();
1812 if (!acpi_battery_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001813 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1814 "acpi_lock_battery_dir() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001815 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001816 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001817 }
1818
1819 result = acpi_bus_register_driver(&acpi_sbs_driver);
1820 if (result < 0) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001821 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1822 "acpi_bus_register_driver() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001823 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001824 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001825 }
1826
Len Brown635227e2006-07-01 16:48:23 -04001827 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001828}
1829
1830static void __exit acpi_sbs_exit(void)
1831{
Rich Townsend3f86b832006-07-01 11:36:54 -04001832 acpi_bus_unregister_driver(&acpi_sbs_driver);
1833
Vladimir Lebedev72206232007-03-19 17:45:50 +03001834 acpi_sbs_rmdirs();
Rich Townsend3f86b832006-07-01 11:36:54 -04001835
Len Brown635227e2006-07-01 16:48:23 -04001836 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001837}
1838
1839module_init(acpi_sbs_init);
1840module_exit(acpi_sbs_exit);