Commit | Line | Data |
---|---|---|
3f86b832 RT |
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); |