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 | ||
3f86b832 RT |
190 | data.word = 0; |
191 | ||
192 | result = smbus->adapter.algo-> | |
193 | smbus_xfer(&smbus->adapter, | |
194 | ACPI_SB_SMBUS_ADDR, | |
195 | 0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data); | |
196 | ||
197 | err_number = (data.word & 0x000f); | |
198 | ||
199 | switch (data.word & 0x000f) { | |
200 | case 0x0000: | |
201 | err_str = "unexpected bus error"; | |
202 | break; | |
203 | case 0x0001: | |
204 | err_str = "busy"; | |
205 | break; | |
206 | case 0x0002: | |
207 | err_str = "reserved command"; | |
208 | break; | |
209 | case 0x0003: | |
210 | err_str = "unsupported command"; | |
211 | break; | |
212 | case 0x0004: | |
213 | err_str = "access denied"; | |
214 | break; | |
215 | case 0x0005: | |
216 | err_str = "overflow/underflow"; | |
217 | break; | |
218 | case 0x0006: | |
219 | err_str = "bad size"; | |
220 | break; | |
221 | case 0x0007: | |
222 | err_str = "unknown error"; | |
223 | break; | |
224 | default: | |
225 | err_str = "unrecognized error"; | |
226 | } | |
227 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
228 | "%s: ret %i, err %i\n", err_str, result, err_number)); | |
229 | } | |
230 | ||
231 | static int | |
232 | acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func, | |
233 | u16 * word, | |
234 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | |
235 | { | |
236 | union i2c_smbus_data data; | |
237 | int result = 0; | |
238 | int i; | |
239 | ||
3f86b832 RT |
240 | if (err_handler == NULL) { |
241 | err_handler = acpi_battery_smbus_err_handler; | |
242 | } | |
243 | ||
244 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | |
245 | result = | |
246 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | |
247 | I2C_SMBUS_READ, func, | |
248 | I2C_SMBUS_WORD_DATA, &data); | |
249 | if (result) { | |
250 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
251 | "try %i: smbus->adapter.algo->smbus_xfer() failed\n", | |
252 | i)); | |
253 | if (err_handler) { | |
254 | err_handler(smbus); | |
255 | } | |
256 | } else { | |
257 | *word = data.word; | |
258 | break; | |
259 | } | |
260 | } | |
261 | ||
635227ee | 262 | return result; |
3f86b832 RT |
263 | } |
264 | ||
265 | static int | |
266 | acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func, | |
267 | char *str, | |
268 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | |
269 | { | |
270 | union i2c_smbus_data data; | |
271 | int result = 0; | |
272 | int i; | |
273 | ||
3f86b832 RT |
274 | if (err_handler == NULL) { |
275 | err_handler = acpi_battery_smbus_err_handler; | |
276 | } | |
277 | ||
278 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | |
279 | result = | |
280 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | |
281 | I2C_SMBUS_READ, func, | |
282 | I2C_SMBUS_BLOCK_DATA, | |
283 | &data); | |
284 | if (result) { | |
285 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
286 | "try %i: smbus->adapter.algo->smbus_xfer() failed\n", | |
287 | i)); | |
288 | if (err_handler) { | |
289 | err_handler(smbus); | |
290 | } | |
291 | } else { | |
292 | strncpy(str, (const char *)data.block + 1, | |
293 | data.block[0]); | |
294 | str[data.block[0]] = 0; | |
295 | break; | |
296 | } | |
297 | } | |
298 | ||
635227ee | 299 | return result; |
3f86b832 RT |
300 | } |
301 | ||
302 | static int | |
303 | acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func, | |
304 | int word, | |
305 | void (*err_handler) (struct acpi_ec_smbus * smbus)) | |
306 | { | |
307 | union i2c_smbus_data data; | |
308 | int result = 0; | |
309 | int i; | |
310 | ||
3f86b832 RT |
311 | if (err_handler == NULL) { |
312 | err_handler = acpi_battery_smbus_err_handler; | |
313 | } | |
314 | ||
315 | data.word = word; | |
316 | ||
317 | for (i = 0; i < MAX_SMBUS_ERR; i++) { | |
318 | result = | |
319 | smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, | |
320 | I2C_SMBUS_WRITE, func, | |
321 | I2C_SMBUS_WORD_DATA, &data); | |
322 | if (result) { | |
323 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
324 | "try %i: smbus->adapter.algo" | |
325 | "->smbus_xfer() failed\n", i)); | |
326 | if (err_handler) { | |
327 | err_handler(smbus); | |
328 | } | |
329 | } else { | |
330 | break; | |
331 | } | |
332 | } | |
333 | ||
635227ee | 334 | return result; |
3f86b832 RT |
335 | } |
336 | ||
337 | /* -------------------------------------------------------------------------- | |
338 | Smart Battery System Management | |
339 | -------------------------------------------------------------------------- */ | |
340 | ||
341 | /* Smart Battery */ | |
342 | ||
343 | static int acpi_sbs_generate_event(struct acpi_device *device, | |
344 | int event, int state, char *bid, char *class) | |
345 | { | |
346 | char bid_saved[5]; | |
347 | char class_saved[20]; | |
348 | int result = 0; | |
349 | ||
3f86b832 RT |
350 | strcpy(bid_saved, acpi_device_bid(device)); |
351 | strcpy(class_saved, acpi_device_class(device)); | |
352 | ||
353 | strcpy(acpi_device_bid(device), bid); | |
354 | strcpy(acpi_device_class(device), class); | |
355 | ||
356 | result = acpi_bus_generate_event(device, event, state); | |
357 | ||
358 | strcpy(acpi_device_bid(device), bid_saved); | |
359 | strcpy(acpi_device_class(device), class_saved); | |
360 | ||
635227ee | 361 | return result; |
3f86b832 RT |
362 | } |
363 | ||
364 | static int acpi_battery_get_present(struct acpi_battery *battery) | |
365 | { | |
366 | s16 state; | |
367 | int result = 0; | |
368 | int is_present = 0; | |
369 | ||
3f86b832 RT |
370 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, |
371 | ACPI_SBSM_SMBUS_ADDR, 0x01, | |
372 | &state, NULL); | |
373 | if (result) { | |
374 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
375 | "acpi_sbs_smbus_read_word() failed")); | |
376 | } | |
377 | if (!result) { | |
378 | is_present = (state & 0x000f) & (1 << battery->id); | |
379 | } | |
380 | battery->battery_present = is_present; | |
381 | ||
635227ee | 382 | return result; |
3f86b832 RT |
383 | } |
384 | ||
385 | static int acpi_battery_is_present(struct acpi_battery *battery) | |
386 | { | |
387 | return (battery->battery_present); | |
388 | } | |
389 | ||
390 | static int acpi_ac_is_present(struct acpi_sbs *sbs) | |
391 | { | |
392 | return (sbs->ac_present); | |
393 | } | |
394 | ||
395 | static int acpi_battery_select(struct acpi_battery *battery) | |
396 | { | |
397 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | |
398 | int result = 0; | |
399 | s16 state; | |
400 | int foo; | |
401 | ||
3f86b832 RT |
402 | if (battery->sbs->sbsm_present) { |
403 | ||
404 | /* Take special care not to knobble other nibbles of | |
405 | * state (aka selector_state), since | |
406 | * it causes charging to halt on SBSELs */ | |
407 | ||
408 | result = | |
409 | acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, | |
410 | &state, NULL); | |
411 | if (result) { | |
412 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
413 | "acpi_sbs_smbus_read_word() failed\n")); | |
414 | goto end; | |
415 | } | |
416 | ||
417 | foo = (state & 0x0fff) | (1 << (battery->id + 12)); | |
418 | result = | |
419 | acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, | |
420 | foo, NULL); | |
421 | if (result) { | |
422 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
423 | "acpi_sbs_smbus_write_word() failed\n")); | |
424 | goto end; | |
425 | } | |
426 | } | |
427 | ||
428 | end: | |
635227ee | 429 | return result; |
3f86b832 RT |
430 | } |
431 | ||
432 | static int acpi_sbsm_get_info(struct acpi_sbs *sbs) | |
433 | { | |
434 | struct acpi_ec_smbus *smbus = sbs->smbus; | |
435 | int result = 0; | |
436 | s16 battery_system_info; | |
437 | ||
3f86b832 RT |
438 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, |
439 | &battery_system_info, NULL); | |
440 | if (result) { | |
441 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
442 | "acpi_sbs_smbus_read_word() failed\n")); | |
443 | goto end; | |
444 | } | |
445 | ||
446 | sbs->sbsm_batteries_supported = battery_system_info & 0x000f; | |
447 | ||
448 | end: | |
449 | ||
635227ee | 450 | return result; |
3f86b832 RT |
451 | } |
452 | ||
453 | static int acpi_battery_get_info(struct acpi_battery *battery) | |
454 | { | |
455 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | |
456 | int result = 0; | |
457 | s16 battery_mode; | |
458 | s16 specification_info; | |
459 | ||
3f86b832 RT |
460 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, |
461 | &battery_mode, | |
462 | &acpi_battery_smbus_err_handler); | |
463 | if (result) { | |
464 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
465 | "acpi_sbs_smbus_read_word() failed\n")); | |
466 | goto end; | |
467 | } | |
468 | battery->info.capacity_mode = (battery_mode & 0x8000) >> 15; | |
469 | ||
470 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10, | |
471 | &battery->info.full_charge_capacity, | |
472 | &acpi_battery_smbus_err_handler); | |
473 | if (result) { | |
474 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
475 | "acpi_sbs_smbus_read_word() failed\n")); | |
476 | goto end; | |
477 | } | |
478 | ||
479 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18, | |
480 | &battery->info.design_capacity, | |
481 | &acpi_battery_smbus_err_handler); | |
482 | ||
483 | if (result) { | |
484 | goto end; | |
485 | } | |
486 | ||
487 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19, | |
488 | &battery->info.design_voltage, | |
489 | &acpi_battery_smbus_err_handler); | |
490 | if (result) { | |
491 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
492 | "acpi_sbs_smbus_read_word() failed\n")); | |
493 | goto end; | |
494 | } | |
495 | ||
496 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a, | |
497 | &specification_info, | |
498 | &acpi_battery_smbus_err_handler); | |
499 | if (result) { | |
500 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
501 | "acpi_sbs_smbus_read_word() failed\n")); | |
502 | goto end; | |
503 | } | |
504 | ||
505 | switch ((specification_info & 0x0f00) >> 8) { | |
506 | case 1: | |
507 | battery->info.vscale = 10; | |
508 | break; | |
509 | case 2: | |
510 | battery->info.vscale = 100; | |
511 | break; | |
512 | case 3: | |
513 | battery->info.vscale = 1000; | |
514 | break; | |
515 | default: | |
516 | battery->info.vscale = 1; | |
517 | } | |
518 | ||
519 | switch ((specification_info & 0xf000) >> 12) { | |
520 | case 1: | |
521 | battery->info.ipscale = 10; | |
522 | break; | |
523 | case 2: | |
524 | battery->info.ipscale = 100; | |
525 | break; | |
526 | case 3: | |
527 | battery->info.ipscale = 1000; | |
528 | break; | |
529 | default: | |
530 | battery->info.ipscale = 1; | |
531 | } | |
532 | ||
533 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c, | |
534 | &battery->info.serial_number, | |
535 | &acpi_battery_smbus_err_handler); | |
536 | if (result) { | |
537 | goto end; | |
538 | } | |
539 | ||
540 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20, | |
541 | battery->info.manufacturer_name, | |
542 | &acpi_battery_smbus_err_handler); | |
543 | if (result) { | |
544 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
545 | "acpi_sbs_smbus_read_str() failed\n")); | |
546 | goto end; | |
547 | } | |
548 | ||
549 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21, | |
550 | battery->info.device_name, | |
551 | &acpi_battery_smbus_err_handler); | |
552 | if (result) { | |
553 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
554 | "acpi_sbs_smbus_read_str() failed\n")); | |
555 | goto end; | |
556 | } | |
557 | ||
558 | result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22, | |
559 | battery->info.device_chemistry, | |
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 | end: | |
635227ee | 568 | return result; |
3f86b832 RT |
569 | } |
570 | ||
571 | static void acpi_update_delay(struct acpi_sbs *sbs) | |
572 | { | |
3f86b832 RT |
573 | if (sbs->zombie) { |
574 | return; | |
575 | } | |
576 | if (sbs->update_time2 > 0) { | |
577 | msleep(sbs->update_time2 * 1000); | |
578 | } | |
579 | } | |
580 | ||
581 | static int acpi_battery_get_state(struct acpi_battery *battery) | |
582 | { | |
583 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | |
584 | int result = 0; | |
585 | ||
3f86b832 RT |
586 | acpi_update_delay(battery->sbs); |
587 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09, | |
588 | &battery->state.voltage, | |
589 | &acpi_battery_smbus_err_handler); | |
590 | if (result) { | |
591 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
592 | "acpi_sbs_smbus_read_word() failed\n")); | |
593 | goto end; | |
594 | } | |
595 | ||
596 | acpi_update_delay(battery->sbs); | |
597 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a, | |
598 | &battery->state.amperage, | |
599 | &acpi_battery_smbus_err_handler); | |
600 | if (result) { | |
601 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
602 | "acpi_sbs_smbus_read_word() failed\n")); | |
603 | goto end; | |
604 | } | |
605 | ||
606 | acpi_update_delay(battery->sbs); | |
607 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f, | |
608 | &battery->state.remaining_capacity, | |
609 | &acpi_battery_smbus_err_handler); | |
610 | if (result) { | |
611 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
612 | "acpi_sbs_smbus_read_word() failed\n")); | |
613 | goto end; | |
614 | } | |
615 | ||
616 | acpi_update_delay(battery->sbs); | |
617 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12, | |
618 | &battery->state.average_time_to_empty, | |
619 | &acpi_battery_smbus_err_handler); | |
620 | if (result) { | |
621 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
622 | "acpi_sbs_smbus_read_word() failed\n")); | |
623 | goto end; | |
624 | } | |
625 | ||
626 | acpi_update_delay(battery->sbs); | |
627 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13, | |
628 | &battery->state.average_time_to_full, | |
629 | &acpi_battery_smbus_err_handler); | |
630 | if (result) { | |
631 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
632 | "acpi_sbs_smbus_read_word() failed\n")); | |
633 | goto end; | |
634 | } | |
635 | ||
636 | acpi_update_delay(battery->sbs); | |
637 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16, | |
638 | &battery->state.battery_status, | |
639 | &acpi_battery_smbus_err_handler); | |
640 | if (result) { | |
641 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
642 | "acpi_sbs_smbus_read_word() failed\n")); | |
643 | goto end; | |
644 | } | |
645 | ||
646 | acpi_update_delay(battery->sbs); | |
647 | ||
648 | end: | |
635227ee | 649 | return result; |
3f86b832 RT |
650 | } |
651 | ||
652 | static int acpi_battery_get_alarm(struct acpi_battery *battery) | |
653 | { | |
654 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | |
655 | int result = 0; | |
656 | ||
3f86b832 RT |
657 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, |
658 | &battery->alarm.remaining_capacity, | |
659 | &acpi_battery_smbus_err_handler); | |
660 | if (result) { | |
661 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
662 | "acpi_sbs_smbus_read_word() failed\n")); | |
663 | goto end; | |
664 | } | |
665 | ||
666 | acpi_update_delay(battery->sbs); | |
667 | ||
668 | end: | |
669 | ||
635227ee | 670 | return result; |
3f86b832 RT |
671 | } |
672 | ||
673 | static int acpi_battery_set_alarm(struct acpi_battery *battery, | |
674 | unsigned long alarm) | |
675 | { | |
676 | struct acpi_ec_smbus *smbus = battery->sbs->smbus; | |
677 | int result = 0; | |
678 | s16 battery_mode; | |
679 | int foo; | |
680 | ||
3f86b832 RT |
681 | result = acpi_battery_select(battery); |
682 | if (result) { | |
683 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
684 | "acpi_battery_select() failed\n")); | |
685 | goto end; | |
686 | } | |
687 | ||
688 | /* If necessary, enable the alarm */ | |
689 | ||
690 | if (alarm > 0) { | |
691 | result = | |
692 | acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, | |
693 | &battery_mode, | |
694 | &acpi_battery_smbus_err_handler); | |
695 | if (result) { | |
696 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
697 | "acpi_sbs_smbus_read_word() failed\n")); | |
698 | goto end; | |
699 | } | |
700 | ||
701 | result = | |
702 | acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, | |
703 | battery_mode & 0xbfff, | |
704 | &acpi_battery_smbus_err_handler); | |
705 | if (result) { | |
706 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
707 | "acpi_sbs_smbus_write_word() failed\n")); | |
708 | goto end; | |
709 | } | |
710 | } | |
711 | ||
712 | foo = alarm / (battery->info.capacity_mode ? 10 : 1); | |
713 | result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, | |
714 | foo, | |
715 | &acpi_battery_smbus_err_handler); | |
716 | if (result) { | |
717 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
718 | "acpi_sbs_smbus_write_word() failed\n")); | |
719 | goto end; | |
720 | } | |
721 | ||
722 | end: | |
723 | ||
635227ee | 724 | return result; |
3f86b832 RT |
725 | } |
726 | ||
727 | static int acpi_battery_set_mode(struct acpi_battery *battery) | |
728 | { | |
729 | int result = 0; | |
730 | s16 battery_mode; | |
731 | ||
3f86b832 RT |
732 | if (capacity_mode == DEF_CAPACITY_UNIT) { |
733 | goto end; | |
734 | } | |
735 | ||
736 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, | |
737 | ACPI_SB_SMBUS_ADDR, 0x03, | |
738 | &battery_mode, NULL); | |
739 | if (result) { | |
740 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
741 | "acpi_sbs_smbus_read_word() failed\n")); | |
742 | goto end; | |
743 | } | |
744 | ||
745 | if (capacity_mode == MAH_CAPACITY_UNIT) { | |
746 | battery_mode &= 0x7fff; | |
747 | } else { | |
748 | battery_mode |= 0x8000; | |
749 | } | |
750 | result = acpi_sbs_smbus_write_word(battery->sbs->smbus, | |
751 | ACPI_SB_SMBUS_ADDR, 0x03, | |
752 | battery_mode, NULL); | |
753 | if (result) { | |
754 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
755 | "acpi_sbs_smbus_write_word() failed\n")); | |
756 | goto end; | |
757 | } | |
758 | ||
759 | result = acpi_sbs_smbus_read_word(battery->sbs->smbus, | |
760 | ACPI_SB_SMBUS_ADDR, 0x03, | |
761 | &battery_mode, NULL); | |
762 | if (result) { | |
763 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
764 | "acpi_sbs_smbus_read_word() failed\n")); | |
765 | goto end; | |
766 | } | |
767 | ||
768 | end: | |
635227ee | 769 | return result; |
3f86b832 RT |
770 | } |
771 | ||
772 | static int acpi_battery_init(struct acpi_battery *battery) | |
773 | { | |
774 | int result = 0; | |
775 | ||
3f86b832 RT |
776 | result = acpi_battery_select(battery); |
777 | if (result) { | |
778 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
779 | "acpi_battery_init() failed\n")); | |
780 | goto end; | |
781 | } | |
782 | ||
783 | result = acpi_battery_set_mode(battery); | |
784 | if (result) { | |
785 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
786 | "acpi_battery_set_mode() failed\n")); | |
787 | goto end; | |
788 | } | |
789 | ||
790 | result = acpi_battery_get_info(battery); | |
791 | if (result) { | |
792 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
793 | "acpi_battery_get_info() failed\n")); | |
794 | goto end; | |
795 | } | |
796 | ||
797 | result = acpi_battery_get_state(battery); | |
798 | if (result) { | |
799 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
800 | "acpi_battery_get_state() failed\n")); | |
801 | goto end; | |
802 | } | |
803 | ||
804 | result = acpi_battery_get_alarm(battery); | |
805 | if (result) { | |
806 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
807 | "acpi_battery_get_alarm() failed\n")); | |
808 | goto end; | |
809 | } | |
810 | ||
811 | end: | |
635227ee | 812 | return result; |
3f86b832 RT |
813 | } |
814 | ||
815 | static int acpi_ac_get_present(struct acpi_sbs *sbs) | |
816 | { | |
817 | struct acpi_ec_smbus *smbus = sbs->smbus; | |
818 | int result = 0; | |
819 | s16 charger_status; | |
820 | ||
3f86b832 RT |
821 | result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13, |
822 | &charger_status, NULL); | |
823 | ||
824 | if (result) { | |
825 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
826 | "acpi_sbs_smbus_read_word() failed\n")); | |
827 | goto end; | |
828 | } | |
829 | ||
830 | sbs->ac_present = (charger_status & 0x8000) >> 15; | |
831 | ||
832 | end: | |
833 | ||
635227ee | 834 | return result; |
3f86b832 RT |
835 | } |
836 | ||
837 | /* -------------------------------------------------------------------------- | |
838 | FS Interface (/proc/acpi) | |
839 | -------------------------------------------------------------------------- */ | |
840 | ||
841 | /* Generic Routines */ | |
842 | ||
843 | static int | |
844 | acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, | |
845 | struct proc_dir_entry *parent_dir, | |
846 | char *dir_name, | |
847 | struct file_operations *info_fops, | |
848 | struct file_operations *state_fops, | |
849 | struct file_operations *alarm_fops, void *data) | |
850 | { | |
851 | struct proc_dir_entry *entry = NULL; | |
852 | ||
3f86b832 RT |
853 | if (!*dir) { |
854 | *dir = proc_mkdir(dir_name, parent_dir); | |
855 | if (!*dir) { | |
856 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
857 | "proc_mkdir() failed\n")); | |
635227ee | 858 | return -ENODEV; |
3f86b832 RT |
859 | } |
860 | (*dir)->owner = THIS_MODULE; | |
861 | } | |
862 | ||
863 | /* 'info' [R] */ | |
864 | if (info_fops) { | |
865 | entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); | |
866 | if (!entry) { | |
867 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
868 | "create_proc_entry() failed\n")); | |
869 | } else { | |
870 | entry->proc_fops = info_fops; | |
871 | entry->data = data; | |
872 | entry->owner = THIS_MODULE; | |
873 | } | |
874 | } | |
875 | ||
876 | /* 'state' [R] */ | |
877 | if (state_fops) { | |
878 | entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); | |
879 | if (!entry) { | |
880 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
881 | "create_proc_entry() failed\n")); | |
882 | } else { | |
883 | entry->proc_fops = state_fops; | |
884 | entry->data = data; | |
885 | entry->owner = THIS_MODULE; | |
886 | } | |
887 | } | |
888 | ||
889 | /* 'alarm' [R/W] */ | |
890 | if (alarm_fops) { | |
891 | entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir); | |
892 | if (!entry) { | |
893 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
894 | "create_proc_entry() failed\n")); | |
895 | } else { | |
896 | entry->proc_fops = alarm_fops; | |
897 | entry->data = data; | |
898 | entry->owner = THIS_MODULE; | |
899 | } | |
900 | } | |
901 | ||
635227ee | 902 | return 0; |
3f86b832 RT |
903 | } |
904 | ||
905 | static void | |
906 | acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir, | |
907 | struct proc_dir_entry *parent_dir) | |
908 | { | |
3f86b832 RT |
909 | |
910 | if (*dir) { | |
911 | remove_proc_entry(ACPI_SBS_FILE_INFO, *dir); | |
912 | remove_proc_entry(ACPI_SBS_FILE_STATE, *dir); | |
913 | remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir); | |
914 | remove_proc_entry((*dir)->name, parent_dir); | |
915 | *dir = NULL; | |
916 | } | |
917 | ||
918 | } | |
919 | ||
920 | /* Smart Battery Interface */ | |
921 | ||
922 | static struct proc_dir_entry *acpi_battery_dir = NULL; | |
923 | ||
924 | static int acpi_battery_read_info(struct seq_file *seq, void *offset) | |
925 | { | |
926 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | |
927 | int cscale; | |
928 | int result = 0; | |
929 | ||
3f86b832 | 930 | if (battery->sbs->zombie) { |
635227ee | 931 | return -ENODEV; |
3f86b832 RT |
932 | } |
933 | ||
934 | down(&sbs_sem); | |
935 | ||
936 | if (update_mode == REQUEST_UPDATE_MODE) { | |
937 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO); | |
938 | if (result) { | |
939 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
940 | "acpi_sbs_update_run() failed\n")); | |
941 | } | |
942 | } | |
943 | ||
944 | if (acpi_battery_is_present(battery)) { | |
945 | seq_printf(seq, "present: yes\n"); | |
946 | } else { | |
947 | seq_printf(seq, "present: no\n"); | |
948 | goto end; | |
949 | } | |
950 | ||
951 | if (battery->info.capacity_mode) { | |
952 | cscale = battery->info.vscale * battery->info.ipscale; | |
953 | } else { | |
954 | cscale = battery->info.ipscale; | |
955 | } | |
956 | seq_printf(seq, "design capacity: %i%s", | |
957 | battery->info.design_capacity * cscale, | |
958 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | |
959 | ||
960 | seq_printf(seq, "last full capacity: %i%s", | |
961 | battery->info.full_charge_capacity * cscale, | |
962 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | |
963 | ||
964 | seq_printf(seq, "battery technology: rechargeable\n"); | |
965 | ||
966 | seq_printf(seq, "design voltage: %i mV\n", | |
967 | battery->info.design_voltage * battery->info.vscale); | |
968 | ||
969 | seq_printf(seq, "design capacity warning: unknown\n"); | |
970 | seq_printf(seq, "design capacity low: unknown\n"); | |
971 | seq_printf(seq, "capacity granularity 1: unknown\n"); | |
972 | seq_printf(seq, "capacity granularity 2: unknown\n"); | |
973 | ||
974 | seq_printf(seq, "model number: %s\n", | |
975 | battery->info.device_name); | |
976 | ||
977 | seq_printf(seq, "serial number: %i\n", | |
978 | battery->info.serial_number); | |
979 | ||
980 | seq_printf(seq, "battery type: %s\n", | |
981 | battery->info.device_chemistry); | |
982 | ||
983 | seq_printf(seq, "OEM info: %s\n", | |
984 | battery->info.manufacturer_name); | |
985 | ||
986 | end: | |
987 | ||
988 | up(&sbs_sem); | |
989 | ||
635227ee | 990 | return result; |
3f86b832 RT |
991 | } |
992 | ||
993 | static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) | |
994 | { | |
995 | return single_open(file, acpi_battery_read_info, PDE(inode)->data); | |
996 | } | |
997 | ||
998 | static int acpi_battery_read_state(struct seq_file *seq, void *offset) | |
999 | { | |
1000 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | |
1001 | int result = 0; | |
1002 | int cscale; | |
1003 | int foo; | |
1004 | ||
3f86b832 | 1005 | if (battery->sbs->zombie) { |
635227ee | 1006 | return -ENODEV; |
3f86b832 RT |
1007 | } |
1008 | ||
1009 | down(&sbs_sem); | |
1010 | ||
1011 | if (update_mode == REQUEST_UPDATE_MODE) { | |
1012 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE); | |
1013 | if (result) { | |
1014 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1015 | "acpi_sbs_update_run() failed\n")); | |
1016 | } | |
1017 | } | |
1018 | ||
1019 | if (acpi_battery_is_present(battery)) { | |
1020 | seq_printf(seq, "present: yes\n"); | |
1021 | } else { | |
1022 | seq_printf(seq, "present: no\n"); | |
1023 | goto end; | |
1024 | } | |
1025 | ||
1026 | if (battery->info.capacity_mode) { | |
1027 | cscale = battery->info.vscale * battery->info.ipscale; | |
1028 | } else { | |
1029 | cscale = battery->info.ipscale; | |
1030 | } | |
1031 | ||
1032 | if (battery->state.battery_status & 0x0010) { | |
1033 | seq_printf(seq, "capacity state: critical\n"); | |
1034 | } else { | |
1035 | seq_printf(seq, "capacity state: ok\n"); | |
1036 | } | |
1037 | if (battery->state.amperage < 0) { | |
1038 | seq_printf(seq, "charging state: discharging\n"); | |
1039 | foo = battery->state.remaining_capacity * cscale * 60 / | |
1040 | (battery->state.average_time_to_empty == 0 ? 1 : | |
1041 | battery->state.average_time_to_empty); | |
1042 | seq_printf(seq, "present rate: %i%s\n", | |
1043 | foo, battery->info.capacity_mode ? "0 mW" : " mA"); | |
1044 | } else if (battery->state.amperage > 0) { | |
1045 | seq_printf(seq, "charging state: charging\n"); | |
1046 | foo = (battery->info.full_charge_capacity - | |
1047 | battery->state.remaining_capacity) * cscale * 60 / | |
1048 | (battery->state.average_time_to_full == 0 ? 1 : | |
1049 | battery->state.average_time_to_full); | |
1050 | seq_printf(seq, "present rate: %i%s\n", | |
1051 | foo, battery->info.capacity_mode ? "0 mW" : " mA"); | |
1052 | } else { | |
1053 | seq_printf(seq, "charging state: charged\n"); | |
1054 | seq_printf(seq, "present rate: 0 %s\n", | |
1055 | battery->info.capacity_mode ? "mW" : "mA"); | |
1056 | } | |
1057 | ||
1058 | seq_printf(seq, "remaining capacity: %i%s", | |
1059 | battery->state.remaining_capacity * cscale, | |
1060 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | |
1061 | ||
1062 | seq_printf(seq, "present voltage: %i mV\n", | |
1063 | battery->state.voltage * battery->info.vscale); | |
1064 | ||
1065 | end: | |
1066 | ||
1067 | up(&sbs_sem); | |
1068 | ||
635227ee | 1069 | return result; |
3f86b832 RT |
1070 | } |
1071 | ||
1072 | static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) | |
1073 | { | |
1074 | return single_open(file, acpi_battery_read_state, PDE(inode)->data); | |
1075 | } | |
1076 | ||
1077 | static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) | |
1078 | { | |
1079 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | |
1080 | int result = 0; | |
1081 | int cscale; | |
1082 | ||
3f86b832 | 1083 | if (battery->sbs->zombie) { |
635227ee | 1084 | return -ENODEV; |
3f86b832 RT |
1085 | } |
1086 | ||
1087 | down(&sbs_sem); | |
1088 | ||
1089 | if (update_mode == REQUEST_UPDATE_MODE) { | |
1090 | result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM); | |
1091 | if (result) { | |
1092 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1093 | "acpi_sbs_update_run() failed\n")); | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | if (!acpi_battery_is_present(battery)) { | |
1098 | seq_printf(seq, "present: no\n"); | |
1099 | goto end; | |
1100 | } | |
1101 | ||
1102 | if (battery->info.capacity_mode) { | |
1103 | cscale = battery->info.vscale * battery->info.ipscale; | |
1104 | } else { | |
1105 | cscale = battery->info.ipscale; | |
1106 | } | |
1107 | ||
1108 | seq_printf(seq, "alarm: "); | |
1109 | if (battery->alarm.remaining_capacity) { | |
1110 | seq_printf(seq, "%i%s", | |
1111 | battery->alarm.remaining_capacity * cscale, | |
1112 | battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); | |
1113 | } else { | |
1114 | seq_printf(seq, "disabled\n"); | |
1115 | } | |
1116 | ||
1117 | end: | |
1118 | ||
1119 | up(&sbs_sem); | |
1120 | ||
635227ee | 1121 | return result; |
3f86b832 RT |
1122 | } |
1123 | ||
1124 | static ssize_t | |
1125 | acpi_battery_write_alarm(struct file *file, const char __user * buffer, | |
1126 | size_t count, loff_t * ppos) | |
1127 | { | |
1128 | struct seq_file *seq = (struct seq_file *)file->private_data; | |
1129 | struct acpi_battery *battery = (struct acpi_battery *)seq->private; | |
1130 | char alarm_string[12] = { '\0' }; | |
1131 | int result, old_alarm, new_alarm; | |
1132 | ||
3f86b832 | 1133 | if (battery->sbs->zombie) { |
635227ee | 1134 | return -ENODEV; |
3f86b832 RT |
1135 | } |
1136 | ||
1137 | down(&sbs_sem); | |
1138 | ||
1139 | if (!acpi_battery_is_present(battery)) { | |
1140 | result = -ENODEV; | |
1141 | goto end; | |
1142 | } | |
1143 | ||
1144 | if (count > sizeof(alarm_string) - 1) { | |
1145 | result = -EINVAL; | |
1146 | goto end; | |
1147 | } | |
1148 | ||
1149 | if (copy_from_user(alarm_string, buffer, count)) { | |
1150 | result = -EFAULT; | |
1151 | goto end; | |
1152 | } | |
1153 | ||
1154 | alarm_string[count] = 0; | |
1155 | ||
1156 | old_alarm = battery->alarm.remaining_capacity; | |
1157 | new_alarm = simple_strtoul(alarm_string, NULL, 0); | |
1158 | ||
1159 | result = acpi_battery_set_alarm(battery, new_alarm); | |
1160 | if (result) { | |
1161 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1162 | "acpi_battery_set_alarm() failed\n")); | |
1163 | (void)acpi_battery_set_alarm(battery, old_alarm); | |
1164 | goto end; | |
1165 | } | |
1166 | result = acpi_battery_get_alarm(battery); | |
1167 | if (result) { | |
1168 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1169 | "acpi_battery_get_alarm() failed\n")); | |
1170 | (void)acpi_battery_set_alarm(battery, old_alarm); | |
1171 | goto end; | |
1172 | } | |
1173 | ||
1174 | end: | |
1175 | up(&sbs_sem); | |
1176 | ||
1177 | if (result) { | |
635227ee | 1178 | return result; |
3f86b832 | 1179 | } else { |
635227ee | 1180 | return count; |
3f86b832 RT |
1181 | } |
1182 | } | |
1183 | ||
1184 | static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) | |
1185 | { | |
1186 | return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); | |
1187 | } | |
1188 | ||
1189 | static struct file_operations acpi_battery_info_fops = { | |
1190 | .open = acpi_battery_info_open_fs, | |
1191 | .read = seq_read, | |
1192 | .llseek = seq_lseek, | |
1193 | .release = single_release, | |
1194 | .owner = THIS_MODULE, | |
1195 | }; | |
1196 | ||
1197 | static struct file_operations acpi_battery_state_fops = { | |
1198 | .open = acpi_battery_state_open_fs, | |
1199 | .read = seq_read, | |
1200 | .llseek = seq_lseek, | |
1201 | .release = single_release, | |
1202 | .owner = THIS_MODULE, | |
1203 | }; | |
1204 | ||
1205 | static struct file_operations acpi_battery_alarm_fops = { | |
1206 | .open = acpi_battery_alarm_open_fs, | |
1207 | .read = seq_read, | |
1208 | .write = acpi_battery_write_alarm, | |
1209 | .llseek = seq_lseek, | |
1210 | .release = single_release, | |
1211 | .owner = THIS_MODULE, | |
1212 | }; | |
1213 | ||
1214 | /* Legacy AC Adapter Interface */ | |
1215 | ||
1216 | static struct proc_dir_entry *acpi_ac_dir = NULL; | |
1217 | ||
1218 | static int acpi_ac_read_state(struct seq_file *seq, void *offset) | |
1219 | { | |
1220 | struct acpi_sbs *sbs = (struct acpi_sbs *)seq->private; | |
1221 | int result; | |
1222 | ||
3f86b832 | 1223 | if (sbs->zombie) { |
635227ee | 1224 | return -ENODEV; |
3f86b832 RT |
1225 | } |
1226 | ||
1227 | down(&sbs_sem); | |
1228 | ||
1229 | if (update_mode == REQUEST_UPDATE_MODE) { | |
1230 | result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE); | |
1231 | if (result) { | |
1232 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1233 | "acpi_sbs_update_run() failed\n")); | |
1234 | } | |
1235 | } | |
1236 | ||
1237 | seq_printf(seq, "state: %s\n", | |
1238 | sbs->ac_present ? "on-line" : "off-line"); | |
1239 | ||
1240 | up(&sbs_sem); | |
1241 | ||
635227ee | 1242 | return 0; |
3f86b832 RT |
1243 | } |
1244 | ||
1245 | static int acpi_ac_state_open_fs(struct inode *inode, struct file *file) | |
1246 | { | |
1247 | return single_open(file, acpi_ac_read_state, PDE(inode)->data); | |
1248 | } | |
1249 | ||
1250 | static struct file_operations acpi_ac_state_fops = { | |
1251 | .open = acpi_ac_state_open_fs, | |
1252 | .read = seq_read, | |
1253 | .llseek = seq_lseek, | |
1254 | .release = single_release, | |
1255 | .owner = THIS_MODULE, | |
1256 | }; | |
1257 | ||
1258 | /* -------------------------------------------------------------------------- | |
1259 | Driver Interface | |
1260 | -------------------------------------------------------------------------- */ | |
1261 | ||
1262 | /* Smart Battery */ | |
1263 | ||
1264 | static int acpi_battery_add(struct acpi_sbs *sbs, int id) | |
1265 | { | |
1266 | int is_present; | |
1267 | int result; | |
1268 | char dir_name[32]; | |
1269 | struct acpi_battery *battery; | |
1270 | ||
3f86b832 RT |
1271 | battery = &sbs->battery[id]; |
1272 | ||
1273 | battery->alive = 0; | |
1274 | ||
1275 | battery->init_state = 0; | |
1276 | battery->id = id; | |
1277 | battery->sbs = sbs; | |
1278 | ||
1279 | result = acpi_battery_select(battery); | |
1280 | if (result) { | |
1281 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1282 | "acpi_battery_select() failed\n")); | |
1283 | goto end; | |
1284 | } | |
1285 | ||
1286 | result = acpi_battery_get_present(battery); | |
1287 | if (result) { | |
1288 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1289 | "acpi_battery_get_present() failed\n")); | |
1290 | goto end; | |
1291 | } | |
1292 | ||
1293 | is_present = acpi_battery_is_present(battery); | |
1294 | ||
1295 | if (is_present) { | |
1296 | result = acpi_battery_init(battery); | |
1297 | if (result) { | |
1298 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1299 | "acpi_battery_init() failed\n")); | |
1300 | goto end; | |
1301 | } | |
1302 | battery->init_state = 1; | |
1303 | } | |
1304 | ||
1305 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | |
1306 | ||
1307 | result = acpi_sbs_generic_add_fs(&battery->battery_entry, | |
1308 | acpi_battery_dir, | |
1309 | dir_name, | |
1310 | &acpi_battery_info_fops, | |
1311 | &acpi_battery_state_fops, | |
1312 | &acpi_battery_alarm_fops, battery); | |
1313 | if (result) { | |
1314 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1315 | "acpi_sbs_generic_add_fs() failed\n")); | |
1316 | goto end; | |
1317 | } | |
1318 | battery->alive = 1; | |
1319 | ||
1320 | end: | |
635227ee | 1321 | return result; |
3f86b832 RT |
1322 | } |
1323 | ||
1324 | static void acpi_battery_remove(struct acpi_sbs *sbs, int id) | |
1325 | { | |
3f86b832 RT |
1326 | |
1327 | if (sbs->battery[id].battery_entry) { | |
1328 | acpi_sbs_generic_remove_fs(&(sbs->battery[id].battery_entry), | |
1329 | acpi_battery_dir); | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | static int acpi_ac_add(struct acpi_sbs *sbs) | |
1334 | { | |
1335 | int result; | |
1336 | ||
3f86b832 RT |
1337 | result = acpi_ac_get_present(sbs); |
1338 | if (result) { | |
1339 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1340 | "acpi_ac_get_present() failed\n")); | |
1341 | goto end; | |
1342 | } | |
1343 | ||
1344 | result = acpi_sbs_generic_add_fs(&sbs->ac_entry, | |
1345 | acpi_ac_dir, | |
1346 | ACPI_AC_DIR_NAME, | |
1347 | NULL, &acpi_ac_state_fops, NULL, sbs); | |
1348 | if (result) { | |
1349 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1350 | "acpi_sbs_generic_add_fs() failed\n")); | |
1351 | goto end; | |
1352 | } | |
1353 | ||
1354 | end: | |
1355 | ||
635227ee | 1356 | return result; |
3f86b832 RT |
1357 | } |
1358 | ||
1359 | static void acpi_ac_remove(struct acpi_sbs *sbs) | |
1360 | { | |
3f86b832 RT |
1361 | |
1362 | if (sbs->ac_entry) { | |
1363 | acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir); | |
1364 | } | |
1365 | } | |
1366 | ||
1367 | static void acpi_sbs_update_queue_run(unsigned long data) | |
1368 | { | |
3f86b832 RT |
1369 | acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data); |
1370 | } | |
1371 | ||
1372 | static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) | |
1373 | { | |
1374 | struct acpi_battery *battery; | |
1375 | int result = 0; | |
1376 | int old_ac_present; | |
1377 | int old_battery_present; | |
1378 | int new_ac_present; | |
1379 | int new_battery_present; | |
1380 | int id; | |
1381 | char dir_name[32]; | |
1382 | int do_battery_init, do_ac_init; | |
1383 | s16 old_remaining_capacity; | |
1384 | ||
3f86b832 RT |
1385 | if (sbs->zombie) { |
1386 | goto end; | |
1387 | } | |
1388 | ||
1389 | old_ac_present = acpi_ac_is_present(sbs); | |
1390 | ||
1391 | result = acpi_ac_get_present(sbs); | |
1392 | if (result) { | |
1393 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1394 | "acpi_ac_get_present() failed\n")); | |
1395 | } | |
1396 | ||
1397 | new_ac_present = acpi_ac_is_present(sbs); | |
1398 | ||
1399 | do_ac_init = (old_ac_present != new_ac_present); | |
1400 | ||
1401 | if (data_type == DATA_TYPE_AC_STATE) { | |
1402 | goto end; | |
1403 | } | |
1404 | ||
1405 | for (id = 0; id < MAX_SBS_BAT; id++) { | |
1406 | battery = &sbs->battery[id]; | |
1407 | if (battery->alive == 0) { | |
1408 | continue; | |
1409 | } | |
1410 | ||
1411 | old_remaining_capacity = battery->state.remaining_capacity; | |
1412 | ||
1413 | old_battery_present = acpi_battery_is_present(battery); | |
1414 | ||
1415 | result = acpi_battery_select(battery); | |
1416 | if (result) { | |
1417 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1418 | "acpi_battery_select() failed\n")); | |
1419 | } | |
1420 | if (sbs->zombie) { | |
1421 | goto end; | |
1422 | } | |
1423 | ||
1424 | result = acpi_battery_get_present(battery); | |
1425 | if (result) { | |
1426 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1427 | "acpi_battery_get_present() failed\n")); | |
1428 | } | |
1429 | if (sbs->zombie) { | |
1430 | goto end; | |
1431 | } | |
1432 | ||
1433 | new_battery_present = acpi_battery_is_present(battery); | |
1434 | ||
1435 | do_battery_init = ((old_battery_present != new_battery_present) | |
1436 | && new_battery_present); | |
1437 | ||
1438 | if (sbs->zombie) { | |
1439 | goto end; | |
1440 | } | |
1441 | if (do_ac_init || do_battery_init || | |
1442 | update_info_mode || sbs->update_info_mode) { | |
1443 | if (sbs->update_info_mode) { | |
1444 | sbs->update_info_mode = 0; | |
1445 | } else { | |
1446 | sbs->update_info_mode = 1; | |
1447 | } | |
1448 | result = acpi_battery_init(battery); | |
1449 | if (result) { | |
1450 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1451 | "acpi_battery_init() " | |
1452 | "failed\n")); | |
1453 | } | |
1454 | } | |
1455 | if (data_type == DATA_TYPE_INFO) { | |
1456 | continue; | |
1457 | } | |
1458 | ||
1459 | if (sbs->zombie) { | |
1460 | goto end; | |
1461 | } | |
1462 | if (new_battery_present) { | |
1463 | result = acpi_battery_get_alarm(battery); | |
1464 | if (result) { | |
1465 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1466 | "acpi_battery_get_alarm() " | |
1467 | "failed\n")); | |
1468 | } | |
1469 | if (data_type == DATA_TYPE_ALARM) { | |
1470 | continue; | |
1471 | } | |
1472 | ||
1473 | result = acpi_battery_get_state(battery); | |
1474 | if (result) { | |
1475 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1476 | "acpi_battery_get_state() " | |
1477 | "failed\n")); | |
1478 | } | |
1479 | } | |
1480 | if (sbs->zombie) { | |
1481 | goto end; | |
1482 | } | |
1483 | if (data_type != DATA_TYPE_COMMON) { | |
1484 | continue; | |
1485 | } | |
1486 | ||
1487 | if (old_battery_present != new_battery_present) { | |
1488 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | |
1489 | result = acpi_sbs_generate_event(sbs->device, | |
1490 | ACPI_SBS_BATTERY_NOTIFY_STATUS, | |
1491 | new_battery_present, | |
1492 | dir_name, | |
1493 | ACPI_BATTERY_CLASS); | |
1494 | if (result) { | |
1495 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1496 | "acpi_sbs_generate_event() " | |
1497 | "failed\n")); | |
1498 | } | |
1499 | } | |
1500 | if (old_remaining_capacity != battery->state.remaining_capacity) { | |
1501 | (void)sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); | |
1502 | result = acpi_sbs_generate_event(sbs->device, | |
1503 | ACPI_SBS_BATTERY_NOTIFY_STATUS, | |
1504 | new_battery_present, | |
1505 | dir_name, | |
1506 | ACPI_BATTERY_CLASS); | |
1507 | if (result) { | |
1508 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1509 | "acpi_sbs_generate_event() failed\n")); | |
1510 | } | |
1511 | } | |
1512 | ||
1513 | } | |
1514 | if (sbs->zombie) { | |
1515 | goto end; | |
1516 | } | |
1517 | if (data_type != DATA_TYPE_COMMON) { | |
1518 | goto end; | |
1519 | } | |
1520 | ||
1521 | if (old_ac_present != new_ac_present) { | |
1522 | result = acpi_sbs_generate_event(sbs->device, | |
1523 | ACPI_SBS_AC_NOTIFY_STATUS, | |
1524 | new_ac_present, | |
1525 | ACPI_AC_DIR_NAME, | |
1526 | ACPI_AC_CLASS); | |
1527 | if (result) { | |
1528 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1529 | "acpi_sbs_generate_event() failed\n")); | |
1530 | } | |
1531 | } | |
1532 | ||
1533 | end: | |
635227ee | 1534 | return result; |
3f86b832 RT |
1535 | } |
1536 | ||
1537 | static void acpi_sbs_update_queue(void *data) | |
1538 | { | |
1539 | struct acpi_sbs *sbs = data; | |
1540 | unsigned long delay = -1; | |
1541 | int result; | |
1542 | ||
3f86b832 RT |
1543 | if (sbs->zombie) { |
1544 | goto end; | |
1545 | } | |
1546 | ||
1547 | result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON); | |
1548 | if (result) { | |
1549 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1550 | "acpi_sbs_update_run() failed\n")); | |
1551 | } | |
1552 | ||
1553 | if (sbs->zombie) { | |
1554 | goto end; | |
1555 | } | |
1556 | ||
1557 | if (update_mode == REQUEST_UPDATE_MODE) { | |
1558 | goto end; | |
1559 | } | |
1560 | ||
1561 | delay = jiffies + HZ * update_time; | |
1562 | sbs->update_timer.data = (unsigned long)data; | |
1563 | sbs->update_timer.function = acpi_sbs_update_queue_run; | |
1564 | sbs->update_timer.expires = delay; | |
1565 | add_timer(&sbs->update_timer); | |
1566 | end: | |
1567 | ; | |
1568 | } | |
1569 | ||
1570 | static int acpi_sbs_add(struct acpi_device *device) | |
1571 | { | |
1572 | struct acpi_sbs *sbs = NULL; | |
1573 | struct acpi_ec_hc *ec_hc = NULL; | |
1574 | int result, remove_result = 0; | |
1575 | unsigned long sbs_obj; | |
1576 | int id, cnt; | |
1577 | acpi_status status = AE_OK; | |
1578 | ||
3f86b832 RT |
1579 | sbs = kmalloc(sizeof(struct acpi_sbs), GFP_KERNEL); |
1580 | if (!sbs) { | |
1581 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n")); | |
635227ee | 1582 | return -ENOMEM; |
3f86b832 RT |
1583 | } |
1584 | memset(sbs, 0, sizeof(struct acpi_sbs)); | |
1585 | ||
1586 | cnt = 0; | |
1587 | while (cnt < 10) { | |
1588 | cnt++; | |
1589 | ec_hc = acpi_get_ec_hc(device); | |
1590 | if (ec_hc) { | |
1591 | break; | |
1592 | } | |
1593 | msleep(1000); | |
1594 | } | |
1595 | ||
1596 | if (!ec_hc) { | |
1597 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1598 | "acpi_get_ec_hc() failed: " | |
1599 | "NO driver found for EC HC SMBus\n")); | |
1600 | result = -ENODEV; | |
1601 | goto end; | |
1602 | } | |
1603 | ||
1604 | sbs->device = device; | |
1605 | sbs->smbus = ec_hc->smbus; | |
1606 | ||
1607 | strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); | |
1608 | strcpy(acpi_device_class(device), ACPI_SBS_CLASS); | |
1609 | acpi_driver_data(device) = sbs; | |
1610 | ||
1611 | sbs->update_time = 0; | |
1612 | sbs->update_time2 = 0; | |
1613 | ||
1614 | result = acpi_ac_add(sbs); | |
1615 | if (result) { | |
1616 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n")); | |
1617 | goto end; | |
1618 | } | |
1619 | result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); | |
1620 | if (ACPI_FAILURE(result)) { | |
1621 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1622 | "acpi_evaluate_integer() failed\n")); | |
1623 | result = -EIO; | |
1624 | goto end; | |
1625 | } | |
1626 | ||
1627 | if (sbs_obj > 0) { | |
1628 | result = acpi_sbsm_get_info(sbs); | |
1629 | if (result) { | |
1630 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1631 | "acpi_sbsm_get_info() failed\n")); | |
1632 | goto end; | |
1633 | } | |
1634 | sbs->sbsm_present = 1; | |
1635 | } | |
1636 | if (sbs->sbsm_present == 0) { | |
1637 | result = acpi_battery_add(sbs, 0); | |
1638 | if (result) { | |
1639 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1640 | "acpi_battery_add() failed\n")); | |
1641 | goto end; | |
1642 | } | |
1643 | } else { | |
1644 | for (id = 0; id < MAX_SBS_BAT; id++) { | |
1645 | if ((sbs->sbsm_batteries_supported & (1 << id))) { | |
1646 | result = acpi_battery_add(sbs, id); | |
1647 | if (result) { | |
1648 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1649 | "acpi_battery_add() " | |
1650 | "failed\n")); | |
1651 | goto end; | |
1652 | } | |
1653 | } | |
1654 | } | |
1655 | } | |
1656 | ||
1657 | sbs->handle = device->handle; | |
1658 | ||
1659 | init_timer(&sbs->update_timer); | |
1660 | if (update_mode == QUEUE_UPDATE_MODE) { | |
1661 | status = acpi_os_execute(OSL_GPE_HANDLER, | |
1662 | acpi_sbs_update_queue, (void *)sbs); | |
1663 | if (status != AE_OK) { | |
1664 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1665 | "acpi_os_execute() failed\n")); | |
1666 | } | |
1667 | } | |
1668 | sbs->update_time = update_time; | |
1669 | sbs->update_time2 = update_time2; | |
1670 | ||
1671 | printk(KERN_INFO PREFIX "%s [%s]\n", | |
1672 | acpi_device_name(device), acpi_device_bid(device)); | |
1673 | ||
1674 | end: | |
1675 | if (result) { | |
1676 | remove_result = acpi_sbs_remove(device, 0); | |
1677 | if (remove_result) { | |
1678 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1679 | "acpi_sbs_remove() failed\n")); | |
1680 | } | |
1681 | } | |
1682 | ||
635227ee | 1683 | return result; |
3f86b832 RT |
1684 | } |
1685 | ||
1686 | int acpi_sbs_remove(struct acpi_device *device, int type) | |
1687 | { | |
1688 | struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); | |
1689 | int id; | |
1690 | ||
3f86b832 | 1691 | if (!device || !sbs) { |
635227ee | 1692 | return -EINVAL; |
3f86b832 RT |
1693 | } |
1694 | ||
1695 | sbs->zombie = 1; | |
1696 | sbs->update_time = 0; | |
1697 | sbs->update_time2 = 0; | |
1698 | del_timer_sync(&sbs->update_timer); | |
1699 | acpi_os_wait_events_complete(NULL); | |
1700 | del_timer_sync(&sbs->update_timer); | |
1701 | ||
1702 | for (id = 0; id < MAX_SBS_BAT; id++) { | |
1703 | acpi_battery_remove(sbs, id); | |
1704 | } | |
1705 | ||
1706 | acpi_ac_remove(sbs); | |
1707 | ||
1708 | kfree(sbs); | |
1709 | ||
635227ee | 1710 | return 0; |
3f86b832 RT |
1711 | } |
1712 | ||
1713 | static int __init acpi_sbs_init(void) | |
1714 | { | |
1715 | int result = 0; | |
1716 | ||
b20d2aeb LB |
1717 | if (acpi_disabled) |
1718 | return -ENODEV; | |
1719 | ||
3f86b832 RT |
1720 | init_MUTEX(&sbs_sem); |
1721 | ||
1722 | if (capacity_mode != DEF_CAPACITY_UNIT | |
1723 | && capacity_mode != MAH_CAPACITY_UNIT | |
1724 | && capacity_mode != MWH_CAPACITY_UNIT) { | |
1725 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: " | |
1726 | "invalid capacity_mode = %d\n", | |
1727 | capacity_mode)); | |
635227ee | 1728 | return -EINVAL; |
3f86b832 RT |
1729 | } |
1730 | ||
1731 | acpi_ac_dir = acpi_lock_ac_dir(); | |
1732 | if (!acpi_ac_dir) { | |
1733 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1734 | "acpi_lock_ac_dir() failed\n")); | |
635227ee | 1735 | return -ENODEV; |
3f86b832 RT |
1736 | } |
1737 | ||
1738 | acpi_battery_dir = acpi_lock_battery_dir(); | |
1739 | if (!acpi_battery_dir) { | |
1740 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1741 | "acpi_lock_battery_dir() failed\n")); | |
635227ee | 1742 | return -ENODEV; |
3f86b832 RT |
1743 | } |
1744 | ||
1745 | result = acpi_bus_register_driver(&acpi_sbs_driver); | |
1746 | if (result < 0) { | |
1747 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1748 | "acpi_bus_register_driver() failed\n")); | |
635227ee | 1749 | return -ENODEV; |
3f86b832 RT |
1750 | } |
1751 | ||
635227ee | 1752 | return 0; |
3f86b832 RT |
1753 | } |
1754 | ||
1755 | static void __exit acpi_sbs_exit(void) | |
1756 | { | |
3f86b832 RT |
1757 | |
1758 | acpi_bus_unregister_driver(&acpi_sbs_driver); | |
1759 | ||
1760 | acpi_unlock_ac_dir(acpi_ac_dir); | |
1761 | acpi_ac_dir = NULL; | |
1762 | acpi_unlock_battery_dir(acpi_battery_dir); | |
1763 | acpi_battery_dir = NULL; | |
1764 | ||
635227ee | 1765 | return; |
3f86b832 RT |
1766 | } |
1767 | ||
1768 | module_init(acpi_sbs_init); | |
1769 | module_exit(acpi_sbs_exit); |