blob: 7bb8c62fb92cf7b78bf325580edd08ab7194dcf1 [file] [log] [blame]
Rich Townsend3f86b832006-07-01 11:36:54 -04001/*
2 * acpi_sbs.c - ACPI Smart Battery System Driver ($Revision: 1.16 $)
3 *
4 * Copyright (c) 2005 Rich Townsend <rhdt@bartol.udel.edu>
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
24
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/kernel.h>
29#include <linux/proc_fs.h>
30#include <linux/seq_file.h>
31#include <asm/uaccess.h>
32#include <linux/acpi.h>
Vladimir Lebedev6d157022007-03-19 17:45:50 +030033#include <linux/timer.h>
Vladimir Lebedev72206232007-03-19 17:45:50 +030034#include <linux/jiffies.h>
Rich Townsend3f86b832006-07-01 11:36:54 -040035#include <linux/delay.h>
36
Alexey Starikovskiy91087df2007-09-26 19:43:28 +040037#include "sbshc.h"
38
Rich Townsend3f86b832006-07-01 11:36:54 -040039#define ACPI_SBS_COMPONENT 0x00080000
40#define ACPI_SBS_CLASS "sbs"
41#define ACPI_AC_CLASS "ac_adapter"
42#define ACPI_BATTERY_CLASS "battery"
Rich Townsend3f86b832006-07-01 11:36:54 -040043#define ACPI_SBS_DEVICE_NAME "Smart Battery System"
44#define ACPI_SBS_FILE_INFO "info"
45#define ACPI_SBS_FILE_STATE "state"
46#define ACPI_SBS_FILE_ALARM "alarm"
47#define ACPI_BATTERY_DIR_NAME "BAT%i"
48#define ACPI_AC_DIR_NAME "AC0"
49#define ACPI_SBC_SMBUS_ADDR 0x9
50#define ACPI_SBSM_SMBUS_ADDR 0xa
51#define ACPI_SB_SMBUS_ADDR 0xb
52#define ACPI_SBS_AC_NOTIFY_STATUS 0x80
53#define ACPI_SBS_BATTERY_NOTIFY_STATUS 0x80
54#define ACPI_SBS_BATTERY_NOTIFY_INFO 0x81
55
56#define _COMPONENT ACPI_SBS_COMPONENT
57
Len Brownf52fd662007-02-12 22:42:12 -050058ACPI_MODULE_NAME("sbs");
Rich Townsend3f86b832006-07-01 11:36:54 -040059
60MODULE_AUTHOR("Rich Townsend");
61MODULE_DESCRIPTION("Smart Battery System ACPI interface driver");
62MODULE_LICENSE("GPL");
63
Vladimir Lebedev6d157022007-03-19 17:45:50 +030064#define DEF_CAPACITY_UNIT 3
65#define MAH_CAPACITY_UNIT 1
66#define MWH_CAPACITY_UNIT 2
67#define CAPACITY_UNIT DEF_CAPACITY_UNIT
68
69#define REQUEST_UPDATE_MODE 1
70#define QUEUE_UPDATE_MODE 2
71
72#define DATA_TYPE_COMMON 0
73#define DATA_TYPE_INFO 1
74#define DATA_TYPE_STATE 2
75#define DATA_TYPE_ALARM 3
76#define DATA_TYPE_AC_STATE 4
77
78extern struct proc_dir_entry *acpi_lock_ac_dir(void);
79extern struct proc_dir_entry *acpi_lock_battery_dir(void);
80extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
81extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
82
83#define MAX_SBS_BAT 4
84#define ACPI_SBS_BLOCK_MAX 32
85
Vladimir Lebedev72206232007-03-19 17:45:50 +030086#define UPDATE_DELAY 10
Rich Townsend3f86b832006-07-01 11:36:54 -040087
Vladimir Lebedev72206232007-03-19 17:45:50 +030088/* 0 - every time, > 0 - by update_time */
89static unsigned int update_time = 120;
Rich Townsend3f86b832006-07-01 11:36:54 -040090
Vladimir Lebedev72206232007-03-19 17:45:50 +030091static unsigned int capacity_mode = CAPACITY_UNIT;
Rich Townsend3f86b832006-07-01 11:36:54 -040092
Vladimir Lebedev72206232007-03-19 17:45:50 +030093module_param(update_time, uint, 0644);
94module_param(capacity_mode, uint, 0444);
Rich Townsend3f86b832006-07-01 11:36:54 -040095
96static int acpi_sbs_add(struct acpi_device *device);
97static int acpi_sbs_remove(struct acpi_device *device, int type);
Vladimir Lebedev72206232007-03-19 17:45:50 +030098static int acpi_sbs_resume(struct acpi_device *device);
Rich Townsend3f86b832006-07-01 11:36:54 -040099
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200100static const struct acpi_device_id sbs_device_ids[] = {
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400101 {"ACPI0002", 0},
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200102 {"", 0},
103};
104MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
105
Rich Townsend3f86b832006-07-01 11:36:54 -0400106static struct acpi_driver acpi_sbs_driver = {
Len Brownc2b67052007-02-12 23:33:40 -0500107 .name = "sbs",
Rich Townsend3f86b832006-07-01 11:36:54 -0400108 .class = ACPI_SBS_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200109 .ids = sbs_device_ids,
Rich Townsend3f86b832006-07-01 11:36:54 -0400110 .ops = {
111 .add = acpi_sbs_add,
112 .remove = acpi_sbs_remove,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300113 .resume = acpi_sbs_resume,
Rich Townsend3f86b832006-07-01 11:36:54 -0400114 },
115};
116
Vladimir Lebedev72206232007-03-19 17:45:50 +0300117struct acpi_ac {
118 int ac_present;
119};
120
Rich Townsend3f86b832006-07-01 11:36:54 -0400121struct acpi_battery_info {
122 int capacity_mode;
123 s16 full_charge_capacity;
124 s16 design_capacity;
125 s16 design_voltage;
126 int vscale;
127 int ipscale;
128 s16 serial_number;
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300129 char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3];
130 char device_name[ACPI_SBS_BLOCK_MAX + 3];
131 char device_chemistry[ACPI_SBS_BLOCK_MAX + 3];
Rich Townsend3f86b832006-07-01 11:36:54 -0400132};
133
134struct acpi_battery_state {
135 s16 voltage;
136 s16 amperage;
137 s16 remaining_capacity;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300138 s16 battery_state;
Rich Townsend3f86b832006-07-01 11:36:54 -0400139};
140
141struct acpi_battery_alarm {
142 s16 remaining_capacity;
143};
144
145struct acpi_battery {
146 int alive;
Rich Townsend3f86b832006-07-01 11:36:54 -0400147 int id;
148 int init_state;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300149 int battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -0400150 struct acpi_sbs *sbs;
151 struct acpi_battery_info info;
152 struct acpi_battery_state state;
153 struct acpi_battery_alarm alarm;
154 struct proc_dir_entry *battery_entry;
155};
156
157struct acpi_sbs {
Rich Townsend3f86b832006-07-01 11:36:54 -0400158 struct acpi_device *device;
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400159 struct acpi_smb_hc *hc;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300160 struct mutex mutex;
Rich Townsend3f86b832006-07-01 11:36:54 -0400161 int sbsm_present;
162 int sbsm_batteries_supported;
Rich Townsend3f86b832006-07-01 11:36:54 -0400163 struct proc_dir_entry *ac_entry;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300164 struct acpi_ac ac;
Rich Townsend3f86b832006-07-01 11:36:54 -0400165 struct acpi_battery battery[MAX_SBS_BAT];
Rich Townsend3f86b832006-07-01 11:36:54 -0400166 int zombie;
Rich Townsend3f86b832006-07-01 11:36:54 -0400167 struct timer_list update_timer;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300168 int run_cnt;
169 int update_proc_flg;
Rich Townsend3f86b832006-07-01 11:36:54 -0400170};
171
Vladimir Lebedev72206232007-03-19 17:45:50 +0300172static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
173static void acpi_sbs_update_time(void *data);
174
Vladimir Lebedev72206232007-03-19 17:45:50 +0300175static int sbs_zombie(struct acpi_sbs *sbs)
176{
177 return (sbs->zombie);
178}
179
180static int sbs_mutex_lock(struct acpi_sbs *sbs)
181{
182 if (sbs_zombie(sbs)) {
183 return -ENODEV;
184 }
185 mutex_lock(&sbs->mutex);
186 return 0;
187}
188
189static void sbs_mutex_unlock(struct acpi_sbs *sbs)
190{
191 mutex_unlock(&sbs->mutex);
192}
193
Rich Townsend3f86b832006-07-01 11:36:54 -0400194/* --------------------------------------------------------------------------
195 Smart Battery System Management
196 -------------------------------------------------------------------------- */
197
Vladimir Lebedev72206232007-03-19 17:45:50 +0300198static int acpi_check_update_proc(struct acpi_sbs *sbs)
199{
200 acpi_status status = AE_OK;
201
202 if (update_time == 0) {
203 sbs->update_proc_flg = 0;
204 return 0;
205 }
206 if (sbs->update_proc_flg == 0) {
207 status = acpi_os_execute(OSL_GPE_HANDLER,
208 acpi_sbs_update_time, sbs);
209 if (status != AE_OK) {
210 ACPI_EXCEPTION((AE_INFO, status,
211 "acpi_os_execute() failed"));
212 return 1;
213 }
214 sbs->update_proc_flg = 1;
215 }
216 return 0;
217}
Rich Townsend3f86b832006-07-01 11:36:54 -0400218
Rich Townsend3f86b832006-07-01 11:36:54 -0400219static int acpi_battery_get_present(struct acpi_battery *battery)
220{
221 s16 state;
222 int result = 0;
223 int is_present = 0;
224
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400225 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
226 ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400227 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300228 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400229 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400230 }
231 if (!result) {
232 is_present = (state & 0x000f) & (1 << battery->id);
233 }
234 battery->battery_present = is_present;
235
Len Brown635227e2006-07-01 16:48:23 -0400236 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400237}
238
Rich Townsend3f86b832006-07-01 11:36:54 -0400239static int acpi_battery_select(struct acpi_battery *battery)
240{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300241 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400242 int result = 0;
243 s16 state;
244 int foo;
245
Vladimir Lebedev72206232007-03-19 17:45:50 +0300246 if (sbs->sbsm_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400247
248 /* Take special care not to knobble other nibbles of
249 * state (aka selector_state), since
250 * it causes charging to halt on SBSELs */
251
252 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400253 acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400254 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300255 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400256 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400257 goto end;
258 }
259
260 foo = (state & 0x0fff) | (1 << (battery->id + 12));
261 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400262 acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&foo, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400263 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300264 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400265 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400266 goto end;
267 }
268 }
269
270 end:
Len Brown635227e2006-07-01 16:48:23 -0400271 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400272}
273
274static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
275{
Rich Townsend3f86b832006-07-01 11:36:54 -0400276 int result = 0;
277 s16 battery_system_info;
278
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400279 result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBSM_SMBUS_ADDR, 0x04,
280 (u8 *)&battery_system_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400281 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300282 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400283 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400284 goto end;
285 }
Alexey Starikovskiyaddad452007-06-23 16:24:48 -0400286 sbs->sbsm_present = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400287 sbs->sbsm_batteries_supported = battery_system_info & 0x000f;
288
289 end:
290
Len Brown635227e2006-07-01 16:48:23 -0400291 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400292}
293
294static int acpi_battery_get_info(struct acpi_battery *battery)
295{
Rich Townsend3f86b832006-07-01 11:36:54 -0400296 int result = 0;
297 s16 battery_mode;
298 s16 specification_info;
299
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400300 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
301 (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400302 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300303 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400304 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400305 goto end;
306 }
307 battery->info.capacity_mode = (battery_mode & 0x8000) >> 15;
308
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400309 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x10,
310 (u8 *)&battery->info.full_charge_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400311 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300312 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400313 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400314 goto end;
315 }
316
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400317 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x18,
318 (u8 *)&battery->info.design_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400319
320 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300321 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400322 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400323 goto end;
324 }
325
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400326 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x19,
327 (u8 *)&battery->info.design_voltage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400328 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300329 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400330 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400331 goto end;
332 }
333
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400334 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x1a,
335 (u8 *)&specification_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400336 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300337 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400338 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400339 goto end;
340 }
341
342 switch ((specification_info & 0x0f00) >> 8) {
343 case 1:
344 battery->info.vscale = 10;
345 break;
346 case 2:
347 battery->info.vscale = 100;
348 break;
349 case 3:
350 battery->info.vscale = 1000;
351 break;
352 default:
353 battery->info.vscale = 1;
354 }
355
356 switch ((specification_info & 0xf000) >> 12) {
357 case 1:
358 battery->info.ipscale = 10;
359 break;
360 case 2:
361 battery->info.ipscale = 100;
362 break;
363 case 3:
364 battery->info.ipscale = 1000;
365 break;
366 default:
367 battery->info.ipscale = 1;
368 }
369
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400370 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x1c,
371 (u8 *)&battery->info.serial_number);
Rich Townsend3f86b832006-07-01 11:36:54 -0400372 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300373 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400374 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400375 goto end;
376 }
377
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400378 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x20,
379 (u8 *)battery->info.manufacturer_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400380 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300381 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
382 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400383 goto end;
384 }
385
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400386 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x21,
387 (u8 *)battery->info.device_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400388 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300389 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
390 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400391 goto end;
392 }
393
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400394 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x22,
395 (u8 *)battery->info.device_chemistry);
Rich Townsend3f86b832006-07-01 11:36:54 -0400396 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300397 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
398 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400399 goto end;
400 }
401
402 end:
Len Brown635227e2006-07-01 16:48:23 -0400403 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400404}
405
Rich Townsend3f86b832006-07-01 11:36:54 -0400406static int acpi_battery_get_state(struct acpi_battery *battery)
407{
Rich Townsend3f86b832006-07-01 11:36:54 -0400408 int result = 0;
409
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400410 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x09,
411 (u8 *)&battery->state.voltage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400412 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300413 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400414 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400415 goto end;
416 }
417
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400418 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0a,
419 (u8 *)&battery->state.amperage);
Rich Townsend3f86b832006-07-01 11:36:54 -0400420 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300421 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400422 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400423 goto end;
424 }
425
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400426 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0f,
427 (u8 *)&battery->state.remaining_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400428 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300429 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400430 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400431 goto end;
432 }
433
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400434 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x16,
435 (u8 *)&battery->state.battery_state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400436 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300437 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400438 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400439 goto end;
440 }
441
Rich Townsend3f86b832006-07-01 11:36:54 -0400442 end:
Len Brown635227e2006-07-01 16:48:23 -0400443 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400444}
445
446static int acpi_battery_get_alarm(struct acpi_battery *battery)
447{
Rich Townsend3f86b832006-07-01 11:36:54 -0400448 int result = 0;
449
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400450 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
451 (u8 *)&battery->alarm.remaining_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400452 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300453 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400454 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400455 goto end;
456 }
457
Rich Townsend3f86b832006-07-01 11:36:54 -0400458 end:
459
Len Brown635227e2006-07-01 16:48:23 -0400460 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400461}
462
463static int acpi_battery_set_alarm(struct acpi_battery *battery,
464 unsigned long alarm)
465{
Rich Townsend3f86b832006-07-01 11:36:54 -0400466 int result = 0;
467 s16 battery_mode;
468 int foo;
469
Rich Townsend3f86b832006-07-01 11:36:54 -0400470 result = acpi_battery_select(battery);
471 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300472 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
473 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400474 goto end;
475 }
476
477 /* If necessary, enable the alarm */
478
479 if (alarm > 0) {
480 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400481 acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
482 (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400483 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300484 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400485 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400486 goto end;
487 }
488
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400489 battery_mode &= 0xbfff;
Rich Townsend3f86b832006-07-01 11:36:54 -0400490 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400491 acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
492 (u8 *)&battery_mode, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400493 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300494 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400495 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400496 goto end;
497 }
498 }
499
500 foo = alarm / (battery->info.capacity_mode ? 10 : 1);
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400501 result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01, (u8 *)&foo, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400502 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300503 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400504 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400505 goto end;
506 }
507
508 end:
509
Len Brown635227e2006-07-01 16:48:23 -0400510 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400511}
512
513static int acpi_battery_set_mode(struct acpi_battery *battery)
514{
515 int result = 0;
516 s16 battery_mode;
517
Rich Townsend3f86b832006-07-01 11:36:54 -0400518 if (capacity_mode == DEF_CAPACITY_UNIT) {
519 goto end;
520 }
521
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400522 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
523 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400524 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300525 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400526 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400527 goto end;
528 }
529
530 if (capacity_mode == MAH_CAPACITY_UNIT) {
531 battery_mode &= 0x7fff;
532 } else {
533 battery_mode |= 0x8000;
534 }
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400535 result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD,
536 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400537 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300538 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400539 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400540 goto end;
541 }
542
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400543 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
544 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400545 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300546 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400547 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400548 goto end;
549 }
550
551 end:
Len Brown635227e2006-07-01 16:48:23 -0400552 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400553}
554
555static int acpi_battery_init(struct acpi_battery *battery)
556{
557 int result = 0;
558
Rich Townsend3f86b832006-07-01 11:36:54 -0400559 result = acpi_battery_select(battery);
560 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300561 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300562 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400563 goto end;
564 }
565
566 result = acpi_battery_set_mode(battery);
567 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300568 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
569 "acpi_battery_set_mode() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400570 goto end;
571 }
572
573 result = acpi_battery_get_info(battery);
574 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300575 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
576 "acpi_battery_get_info() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400577 goto end;
578 }
579
580 result = acpi_battery_get_state(battery);
581 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300582 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
583 "acpi_battery_get_state() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400584 goto end;
585 }
586
587 result = acpi_battery_get_alarm(battery);
588 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300589 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
590 "acpi_battery_get_alarm() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400591 goto end;
592 }
593
594 end:
Len Brown635227e2006-07-01 16:48:23 -0400595 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400596}
597
598static int acpi_ac_get_present(struct acpi_sbs *sbs)
599{
Rich Townsend3f86b832006-07-01 11:36:54 -0400600 int result = 0;
601 s16 charger_status;
602
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400603 result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBC_SMBUS_ADDR, 0x13,
604 (u8 *)&charger_status);
Rich Townsend3f86b832006-07-01 11:36:54 -0400605
606 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300607 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400608 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400609 goto end;
610 }
611
Vladimir Lebedev72206232007-03-19 17:45:50 +0300612 sbs->ac.ac_present = (charger_status & 0x8000) >> 15;
Rich Townsend3f86b832006-07-01 11:36:54 -0400613
614 end:
615
Len Brown635227e2006-07-01 16:48:23 -0400616 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400617}
618
619/* --------------------------------------------------------------------------
620 FS Interface (/proc/acpi)
621 -------------------------------------------------------------------------- */
622
623/* Generic Routines */
624
625static int
626acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
627 struct proc_dir_entry *parent_dir,
628 char *dir_name,
629 struct file_operations *info_fops,
630 struct file_operations *state_fops,
631 struct file_operations *alarm_fops, void *data)
632{
633 struct proc_dir_entry *entry = NULL;
634
Rich Townsend3f86b832006-07-01 11:36:54 -0400635 if (!*dir) {
636 *dir = proc_mkdir(dir_name, parent_dir);
637 if (!*dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300638 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
639 "proc_mkdir() failed"));
Len Brown635227e2006-07-01 16:48:23 -0400640 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400641 }
642 (*dir)->owner = THIS_MODULE;
643 }
644
645 /* 'info' [R] */
646 if (info_fops) {
647 entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
648 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300649 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
650 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400651 } else {
652 entry->proc_fops = info_fops;
653 entry->data = data;
654 entry->owner = THIS_MODULE;
655 }
656 }
657
658 /* 'state' [R] */
659 if (state_fops) {
660 entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
661 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300662 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
663 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400664 } else {
665 entry->proc_fops = state_fops;
666 entry->data = data;
667 entry->owner = THIS_MODULE;
668 }
669 }
670
671 /* 'alarm' [R/W] */
672 if (alarm_fops) {
673 entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
674 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300675 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
676 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400677 } else {
678 entry->proc_fops = alarm_fops;
679 entry->data = data;
680 entry->owner = THIS_MODULE;
681 }
682 }
683
Len Brown635227e2006-07-01 16:48:23 -0400684 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400685}
686
687static void
688acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir,
689 struct proc_dir_entry *parent_dir)
690{
Rich Townsend3f86b832006-07-01 11:36:54 -0400691
692 if (*dir) {
693 remove_proc_entry(ACPI_SBS_FILE_INFO, *dir);
694 remove_proc_entry(ACPI_SBS_FILE_STATE, *dir);
695 remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir);
696 remove_proc_entry((*dir)->name, parent_dir);
697 *dir = NULL;
698 }
699
700}
701
702/* Smart Battery Interface */
703
704static struct proc_dir_entry *acpi_battery_dir = NULL;
705
706static int acpi_battery_read_info(struct seq_file *seq, void *offset)
707{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200708 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300709 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400710 int cscale;
711 int result = 0;
712
Vladimir Lebedev72206232007-03-19 17:45:50 +0300713 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400714 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400715 }
716
Vladimir Lebedev72206232007-03-19 17:45:50 +0300717 result = acpi_check_update_proc(sbs);
718 if (result)
719 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400720
Vladimir Lebedev72206232007-03-19 17:45:50 +0300721 if (update_time == 0) {
722 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
Rich Townsend3f86b832006-07-01 11:36:54 -0400723 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300724 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
725 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400726 }
727 }
728
Vladimir Lebedev72206232007-03-19 17:45:50 +0300729 if (battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400730 seq_printf(seq, "present: yes\n");
731 } else {
732 seq_printf(seq, "present: no\n");
733 goto end;
734 }
735
736 if (battery->info.capacity_mode) {
737 cscale = battery->info.vscale * battery->info.ipscale;
738 } else {
739 cscale = battery->info.ipscale;
740 }
Vladimir Lebedev72206232007-03-19 17:45:50 +0300741 seq_printf(seq, "design capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400742 battery->info.design_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300743 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400744
Vladimir Lebedev72206232007-03-19 17:45:50 +0300745 seq_printf(seq, "last full capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400746 battery->info.full_charge_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300747 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400748
749 seq_printf(seq, "battery technology: rechargeable\n");
750
751 seq_printf(seq, "design voltage: %i mV\n",
752 battery->info.design_voltage * battery->info.vscale);
753
754 seq_printf(seq, "design capacity warning: unknown\n");
755 seq_printf(seq, "design capacity low: unknown\n");
756 seq_printf(seq, "capacity granularity 1: unknown\n");
757 seq_printf(seq, "capacity granularity 2: unknown\n");
758
759 seq_printf(seq, "model number: %s\n",
760 battery->info.device_name);
761
762 seq_printf(seq, "serial number: %i\n",
763 battery->info.serial_number);
764
765 seq_printf(seq, "battery type: %s\n",
766 battery->info.device_chemistry);
767
768 seq_printf(seq, "OEM info: %s\n",
769 battery->info.manufacturer_name);
770
771 end:
772
Vladimir Lebedev72206232007-03-19 17:45:50 +0300773 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400774
Len Brown635227e2006-07-01 16:48:23 -0400775 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400776}
777
778static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
779{
780 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
781}
782
783static int acpi_battery_read_state(struct seq_file *seq, void *offset)
784{
Vladimir Lebedev72206232007-03-19 17:45:50 +0300785 struct acpi_battery *battery = seq->private;
786 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400787 int result = 0;
788 int cscale;
789 int foo;
790
Vladimir Lebedev72206232007-03-19 17:45:50 +0300791 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400792 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400793 }
794
Vladimir Lebedev72206232007-03-19 17:45:50 +0300795 result = acpi_check_update_proc(sbs);
796 if (result)
797 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400798
Vladimir Lebedev72206232007-03-19 17:45:50 +0300799 if (update_time == 0) {
800 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -0400801 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300802 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
803 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400804 }
805 }
806
Vladimir Lebedev72206232007-03-19 17:45:50 +0300807 if (battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400808 seq_printf(seq, "present: yes\n");
809 } else {
810 seq_printf(seq, "present: no\n");
811 goto end;
812 }
813
814 if (battery->info.capacity_mode) {
815 cscale = battery->info.vscale * battery->info.ipscale;
816 } else {
817 cscale = battery->info.ipscale;
818 }
819
Vladimir Lebedev72206232007-03-19 17:45:50 +0300820 if (battery->state.battery_state & 0x0010) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400821 seq_printf(seq, "capacity state: critical\n");
822 } else {
823 seq_printf(seq, "capacity state: ok\n");
824 }
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500825
826 foo = (s16) battery->state.amperage * battery->info.ipscale;
827 if (battery->info.capacity_mode) {
828 foo = foo * battery->info.design_voltage / 1000;
829 }
Rich Townsend3f86b832006-07-01 11:36:54 -0400830 if (battery->state.amperage < 0) {
831 seq_printf(seq, "charging state: discharging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500832 seq_printf(seq, "present rate: %d %s\n",
833 -foo, battery->info.capacity_mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -0400834 } else if (battery->state.amperage > 0) {
835 seq_printf(seq, "charging state: charging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500836 seq_printf(seq, "present rate: %d %s\n",
837 foo, battery->info.capacity_mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -0400838 } else {
839 seq_printf(seq, "charging state: charged\n");
840 seq_printf(seq, "present rate: 0 %s\n",
841 battery->info.capacity_mode ? "mW" : "mA");
842 }
843
Vladimir Lebedev72206232007-03-19 17:45:50 +0300844 seq_printf(seq, "remaining capacity: %i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400845 battery->state.remaining_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300846 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400847
848 seq_printf(seq, "present voltage: %i mV\n",
849 battery->state.voltage * battery->info.vscale);
850
851 end:
852
Vladimir Lebedev72206232007-03-19 17:45:50 +0300853 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400854
Len Brown635227e2006-07-01 16:48:23 -0400855 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400856}
857
858static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
859{
860 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
861}
862
863static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
864{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200865 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300866 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400867 int result = 0;
868 int cscale;
869
Vladimir Lebedev72206232007-03-19 17:45:50 +0300870 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400871 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400872 }
873
Vladimir Lebedev72206232007-03-19 17:45:50 +0300874 result = acpi_check_update_proc(sbs);
875 if (result)
876 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400877
Vladimir Lebedev72206232007-03-19 17:45:50 +0300878 if (update_time == 0) {
879 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
Rich Townsend3f86b832006-07-01 11:36:54 -0400880 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300881 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
882 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400883 }
884 }
885
Vladimir Lebedev72206232007-03-19 17:45:50 +0300886 if (!battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400887 seq_printf(seq, "present: no\n");
888 goto end;
889 }
890
891 if (battery->info.capacity_mode) {
892 cscale = battery->info.vscale * battery->info.ipscale;
893 } else {
894 cscale = battery->info.ipscale;
895 }
896
897 seq_printf(seq, "alarm: ");
898 if (battery->alarm.remaining_capacity) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300899 seq_printf(seq, "%i%s\n",
Rich Townsend3f86b832006-07-01 11:36:54 -0400900 battery->alarm.remaining_capacity * cscale,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300901 battery->info.capacity_mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400902 } else {
903 seq_printf(seq, "disabled\n");
904 }
905
906 end:
907
Vladimir Lebedev72206232007-03-19 17:45:50 +0300908 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400909
Len Brown635227e2006-07-01 16:48:23 -0400910 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400911}
912
913static ssize_t
914acpi_battery_write_alarm(struct file *file, const char __user * buffer,
915 size_t count, loff_t * ppos)
916{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200917 struct seq_file *seq = file->private_data;
918 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300919 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400920 char alarm_string[12] = { '\0' };
921 int result, old_alarm, new_alarm;
922
Vladimir Lebedev72206232007-03-19 17:45:50 +0300923 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400924 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400925 }
926
Vladimir Lebedev72206232007-03-19 17:45:50 +0300927 result = acpi_check_update_proc(sbs);
928 if (result)
929 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400930
Vladimir Lebedev72206232007-03-19 17:45:50 +0300931 if (!battery->battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400932 result = -ENODEV;
933 goto end;
934 }
935
936 if (count > sizeof(alarm_string) - 1) {
937 result = -EINVAL;
938 goto end;
939 }
940
941 if (copy_from_user(alarm_string, buffer, count)) {
942 result = -EFAULT;
943 goto end;
944 }
945
946 alarm_string[count] = 0;
947
948 old_alarm = battery->alarm.remaining_capacity;
949 new_alarm = simple_strtoul(alarm_string, NULL, 0);
950
951 result = acpi_battery_set_alarm(battery, new_alarm);
952 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300953 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
954 "acpi_battery_set_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200955 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -0400956 goto end;
957 }
958 result = acpi_battery_get_alarm(battery);
959 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300960 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
961 "acpi_battery_get_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200962 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -0400963 goto end;
964 }
965
966 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +0300967 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400968
969 if (result) {
Len Brown635227e2006-07-01 16:48:23 -0400970 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400971 } else {
Len Brown635227e2006-07-01 16:48:23 -0400972 return count;
Rich Townsend3f86b832006-07-01 11:36:54 -0400973 }
974}
975
976static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
977{
978 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
979}
980
981static struct file_operations acpi_battery_info_fops = {
982 .open = acpi_battery_info_open_fs,
983 .read = seq_read,
984 .llseek = seq_lseek,
985 .release = single_release,
986 .owner = THIS_MODULE,
987};
988
989static struct file_operations acpi_battery_state_fops = {
990 .open = acpi_battery_state_open_fs,
991 .read = seq_read,
992 .llseek = seq_lseek,
993 .release = single_release,
994 .owner = THIS_MODULE,
995};
996
997static struct file_operations acpi_battery_alarm_fops = {
998 .open = acpi_battery_alarm_open_fs,
999 .read = seq_read,
1000 .write = acpi_battery_write_alarm,
1001 .llseek = seq_lseek,
1002 .release = single_release,
1003 .owner = THIS_MODULE,
1004};
1005
1006/* Legacy AC Adapter Interface */
1007
1008static struct proc_dir_entry *acpi_ac_dir = NULL;
1009
1010static int acpi_ac_read_state(struct seq_file *seq, void *offset)
1011{
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001012 struct acpi_sbs *sbs = seq->private;
Rich Townsend3f86b832006-07-01 11:36:54 -04001013 int result;
1014
Vladimir Lebedev72206232007-03-19 17:45:50 +03001015 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001016 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001017 }
1018
Vladimir Lebedev72206232007-03-19 17:45:50 +03001019 if (update_time == 0) {
1020 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -04001021 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001022 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1023 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001024 }
1025 }
1026
1027 seq_printf(seq, "state: %s\n",
Vladimir Lebedev72206232007-03-19 17:45:50 +03001028 sbs->ac.ac_present ? "on-line" : "off-line");
Rich Townsend3f86b832006-07-01 11:36:54 -04001029
Vladimir Lebedev72206232007-03-19 17:45:50 +03001030 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001031
Len Brown635227e2006-07-01 16:48:23 -04001032 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001033}
1034
1035static int acpi_ac_state_open_fs(struct inode *inode, struct file *file)
1036{
1037 return single_open(file, acpi_ac_read_state, PDE(inode)->data);
1038}
1039
1040static struct file_operations acpi_ac_state_fops = {
1041 .open = acpi_ac_state_open_fs,
1042 .read = seq_read,
1043 .llseek = seq_lseek,
1044 .release = single_release,
1045 .owner = THIS_MODULE,
1046};
1047
1048/* --------------------------------------------------------------------------
1049 Driver Interface
1050 -------------------------------------------------------------------------- */
1051
1052/* Smart Battery */
1053
1054static int acpi_battery_add(struct acpi_sbs *sbs, int id)
1055{
1056 int is_present;
1057 int result;
1058 char dir_name[32];
1059 struct acpi_battery *battery;
1060
Rich Townsend3f86b832006-07-01 11:36:54 -04001061 battery = &sbs->battery[id];
1062
1063 battery->alive = 0;
1064
1065 battery->init_state = 0;
1066 battery->id = id;
1067 battery->sbs = sbs;
1068
1069 result = acpi_battery_select(battery);
1070 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001071 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1072 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001073 goto end;
1074 }
1075
1076 result = acpi_battery_get_present(battery);
1077 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001078 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1079 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001080 goto end;
1081 }
1082
Vladimir Lebedev72206232007-03-19 17:45:50 +03001083 is_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001084
1085 if (is_present) {
1086 result = acpi_battery_init(battery);
1087 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001088 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1089 "acpi_battery_init() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001090 goto end;
1091 }
1092 battery->init_state = 1;
1093 }
1094
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001095 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Rich Townsend3f86b832006-07-01 11:36:54 -04001096
1097 result = acpi_sbs_generic_add_fs(&battery->battery_entry,
1098 acpi_battery_dir,
1099 dir_name,
1100 &acpi_battery_info_fops,
1101 &acpi_battery_state_fops,
1102 &acpi_battery_alarm_fops, battery);
1103 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001104 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1105 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001106 goto end;
1107 }
1108 battery->alive = 1;
1109
Vladimir Lebedev72206232007-03-19 17:45:50 +03001110 printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
1111 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
1112 sbs->battery->battery_present ? "present" : "absent");
1113
Rich Townsend3f86b832006-07-01 11:36:54 -04001114 end:
Len Brown635227e2006-07-01 16:48:23 -04001115 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001116}
1117
1118static void acpi_battery_remove(struct acpi_sbs *sbs, int id)
1119{
Rich Townsend3f86b832006-07-01 11:36:54 -04001120
1121 if (sbs->battery[id].battery_entry) {
1122 acpi_sbs_generic_remove_fs(&(sbs->battery[id].battery_entry),
1123 acpi_battery_dir);
1124 }
1125}
1126
1127static int acpi_ac_add(struct acpi_sbs *sbs)
1128{
1129 int result;
1130
Rich Townsend3f86b832006-07-01 11:36:54 -04001131 result = acpi_ac_get_present(sbs);
1132 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001133 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1134 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001135 goto end;
1136 }
1137
1138 result = acpi_sbs_generic_add_fs(&sbs->ac_entry,
1139 acpi_ac_dir,
1140 ACPI_AC_DIR_NAME,
1141 NULL, &acpi_ac_state_fops, NULL, sbs);
1142 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001143 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1144 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001145 goto end;
1146 }
1147
Vladimir Lebedev72206232007-03-19 17:45:50 +03001148 printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
1149 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
1150 ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line");
1151
Rich Townsend3f86b832006-07-01 11:36:54 -04001152 end:
1153
Len Brown635227e2006-07-01 16:48:23 -04001154 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001155}
1156
1157static void acpi_ac_remove(struct acpi_sbs *sbs)
1158{
Rich Townsend3f86b832006-07-01 11:36:54 -04001159
1160 if (sbs->ac_entry) {
1161 acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir);
1162 }
1163}
1164
Vladimir Lebedev72206232007-03-19 17:45:50 +03001165static void acpi_sbs_update_time_run(unsigned long data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001166{
Vladimir Lebedev72206232007-03-19 17:45:50 +03001167 acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
Rich Townsend3f86b832006-07-01 11:36:54 -04001168}
1169
Vladimir Lebedev72206232007-03-19 17:45:50 +03001170static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001171{
1172 struct acpi_battery *battery;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001173 int result = 0, cnt;
1174 int old_ac_present = -1;
1175 int old_battery_present = -1;
1176 int new_ac_present = -1;
1177 int new_battery_present = -1;
1178 int id_min = 0, id_max = MAX_SBS_BAT - 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001179 char dir_name[32];
Vladimir Lebedev72206232007-03-19 17:45:50 +03001180 int do_battery_init = 0, do_ac_init = 0;
1181 int old_remaining_capacity = 0;
Adrian Bunkbc90a012007-07-09 11:33:15 -07001182 int update_battery = 1;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001183 int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001184
Vladimir Lebedev72206232007-03-19 17:45:50 +03001185 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001186 goto end;
1187 }
1188
Vladimir Lebedev72206232007-03-19 17:45:50 +03001189 if (id >= 0) {
1190 id_min = id_max = id;
1191 }
1192
1193 if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
1194 cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1195 if (sbs->run_cnt % cnt != 0) {
1196 update_battery = 0;
1197 }
1198 }
1199
1200 sbs->run_cnt++;
1201
Vladimir Lebedev72206232007-03-19 17:45:50 +03001202 old_ac_present = sbs->ac.ac_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001203
1204 result = acpi_ac_get_present(sbs);
1205 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001206 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1207 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001208 }
1209
Vladimir Lebedev72206232007-03-19 17:45:50 +03001210 new_ac_present = sbs->ac.ac_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001211
1212 do_ac_init = (old_ac_present != new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001213 if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
1214 do_ac_init = 1;
1215 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001216
Vladimir Lebedev72206232007-03-19 17:45:50 +03001217 if (do_ac_init) {
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001218 result = acpi_bus_generate_proc_event4(ACPI_AC_CLASS,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001219 ACPI_AC_DIR_NAME,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001220 ACPI_SBS_AC_NOTIFY_STATUS,
1221 new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001222 if (result) {
1223 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001224 "acpi_bus_generate_event4() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001225 }
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001226 acpi_bus_generate_netlink_event(ACPI_AC_CLASS, ACPI_AC_DIR_NAME,
1227 ACPI_SBS_AC_NOTIFY_STATUS,
1228 new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001229 }
1230
1231 if (data_type == DATA_TYPE_COMMON) {
1232 if (!do_ac_init && !update_battery) {
1233 goto end;
1234 }
1235 }
1236
1237 if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001238 goto end;
1239 }
1240
Vladimir Lebedev72206232007-03-19 17:45:50 +03001241 for (id = id_min; id <= id_max; id++) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001242 battery = &sbs->battery[id];
1243 if (battery->alive == 0) {
1244 continue;
1245 }
1246
1247 old_remaining_capacity = battery->state.remaining_capacity;
1248
Vladimir Lebedev72206232007-03-19 17:45:50 +03001249 old_battery_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001250
1251 result = acpi_battery_select(battery);
1252 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001253 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1254 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001255 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001256
1257 result = acpi_battery_get_present(battery);
1258 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001259 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1260 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001261 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001262
Vladimir Lebedev72206232007-03-19 17:45:50 +03001263 new_battery_present = battery->battery_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001264
1265 do_battery_init = ((old_battery_present != new_battery_present)
1266 && new_battery_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001267 if (!new_battery_present)
1268 goto event;
1269 if (do_ac_init || do_battery_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001270 result = acpi_battery_init(battery);
1271 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001272 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1273 "acpi_battery_init() "
1274 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001275 }
1276 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001277 if (sbs_zombie(sbs)) {
1278 goto end;
1279 }
1280
1281 if ((data_type == DATA_TYPE_COMMON
1282 || data_type == DATA_TYPE_INFO)
1283 && new_battery_present) {
1284 result = acpi_battery_get_info(battery);
1285 if (result) {
1286 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1287 "acpi_battery_get_info() failed"));
1288 }
1289 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001290 if (data_type == DATA_TYPE_INFO) {
1291 continue;
1292 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001293 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001294 goto end;
1295 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001296
1297 if ((data_type == DATA_TYPE_COMMON
1298 || data_type == DATA_TYPE_STATE)
1299 && new_battery_present) {
1300 result = acpi_battery_get_state(battery);
1301 if (result) {
1302 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1303 "acpi_battery_get_state() failed"));
1304 }
1305 }
1306 if (data_type == DATA_TYPE_STATE) {
1307 goto event;
1308 }
1309 if (sbs_zombie(sbs)) {
1310 goto end;
1311 }
1312
1313 if ((data_type == DATA_TYPE_COMMON
1314 || data_type == DATA_TYPE_ALARM)
1315 && new_battery_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001316 result = acpi_battery_get_alarm(battery);
1317 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001318 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1319 "acpi_battery_get_alarm() "
1320 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001321 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001322 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001323 if (data_type == DATA_TYPE_ALARM) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001324 continue;
1325 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001326 if (sbs_zombie(sbs)) {
1327 goto end;
1328 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001329
Vladimir Lebedev72206232007-03-19 17:45:50 +03001330 event:
1331
1332 if (old_battery_present != new_battery_present || do_ac_init ||
1333 old_remaining_capacity !=
1334 battery->state.remaining_capacity) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001335 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001336 result = acpi_bus_generate_proc_event4(ACPI_BATTERY_CLASS,
Rich Townsend3f86b832006-07-01 11:36:54 -04001337 dir_name,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001338 ACPI_SBS_BATTERY_NOTIFY_STATUS,
1339 new_battery_present);
1340 acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS, dir_name,
1341 ACPI_SBS_BATTERY_NOTIFY_STATUS,
1342 new_battery_present);
Rich Townsend3f86b832006-07-01 11:36:54 -04001343 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001344 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001345 "acpi_bus_generate_proc_event4() "
Vladimir Lebedev68451182007-03-19 17:45:50 +03001346 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001347 }
1348 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001349 }
1350
1351 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001352
Len Brown635227e2006-07-01 16:48:23 -04001353 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001354}
1355
Vladimir Lebedev72206232007-03-19 17:45:50 +03001356static void acpi_sbs_update_time(void *data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001357{
1358 struct acpi_sbs *sbs = data;
1359 unsigned long delay = -1;
1360 int result;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001361 unsigned int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001362
Vladimir Lebedev72206232007-03-19 17:45:50 +03001363 if (sbs_mutex_lock(sbs))
1364 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001365
Vladimir Lebedev72206232007-03-19 17:45:50 +03001366 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
Rich Townsend3f86b832006-07-01 11:36:54 -04001367 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001368 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1369 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001370 }
1371
Vladimir Lebedev72206232007-03-19 17:45:50 +03001372 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001373 goto end;
1374 }
1375
Vladimir Lebedev72206232007-03-19 17:45:50 +03001376 if (!up_tm) {
1377 if (timer_pending(&sbs->update_timer))
1378 del_timer(&sbs->update_timer);
1379 } else {
1380 delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1381 delay = jiffies + HZ * delay;
1382 if (timer_pending(&sbs->update_timer)) {
1383 mod_timer(&sbs->update_timer, delay);
1384 } else {
1385 sbs->update_timer.data = (unsigned long)data;
1386 sbs->update_timer.function = acpi_sbs_update_time_run;
1387 sbs->update_timer.expires = delay;
1388 add_timer(&sbs->update_timer);
1389 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001390 }
1391
Rich Townsend3f86b832006-07-01 11:36:54 -04001392 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001393
1394 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001395}
1396
1397static int acpi_sbs_add(struct acpi_device *device)
1398{
1399 struct acpi_sbs *sbs = NULL;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001400 int result = 0, remove_result = 0;
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001401 int id;
Rich Townsend3f86b832006-07-01 11:36:54 -04001402
Burman Yan36bcbec2006-12-19 12:56:11 -08001403 sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
Rich Townsend3f86b832006-07-01 11:36:54 -04001404 if (!sbs) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001405 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
1406 result = -ENOMEM;
1407 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001408 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001409
Vladimir Lebedev72206232007-03-19 17:45:50 +03001410 mutex_init(&sbs->mutex);
1411
1412 sbs_mutex_lock(sbs);
1413
Rich Townsend3f86b832006-07-01 11:36:54 -04001414 sbs->device = device;
Alexey Starikovskiy91087df2007-09-26 19:43:28 +04001415 sbs->hc = acpi_driver_data(device->parent);
Rich Townsend3f86b832006-07-01 11:36:54 -04001416
1417 strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
1418 strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
1419 acpi_driver_data(device) = sbs;
1420
Rich Townsend3f86b832006-07-01 11:36:54 -04001421 result = acpi_ac_add(sbs);
1422 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001423 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001424 goto end;
1425 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001426
Alexey Starikovskiyaddad452007-06-23 16:24:48 -04001427 acpi_sbsm_get_info(sbs);
1428
1429 if (!sbs->sbsm_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001430 result = acpi_battery_add(sbs, 0);
1431 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001432 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1433 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001434 goto end;
1435 }
1436 } else {
1437 for (id = 0; id < MAX_SBS_BAT; id++) {
1438 if ((sbs->sbsm_batteries_supported & (1 << id))) {
1439 result = acpi_battery_add(sbs, id);
1440 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001441 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001442 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001443 goto end;
1444 }
1445 }
1446 }
1447 }
1448
Rich Townsend3f86b832006-07-01 11:36:54 -04001449 init_timer(&sbs->update_timer);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001450 result = acpi_check_update_proc(sbs);
1451 if (result)
1452 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001453
1454 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001455
1456 sbs_mutex_unlock(sbs);
1457
Rich Townsend3f86b832006-07-01 11:36:54 -04001458 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001459 remove_result = acpi_sbs_remove(device, 0);
1460 if (remove_result) {
1461 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1462 "acpi_sbs_remove() failed"));
1463 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001464 }
1465
Len Brown635227e2006-07-01 16:48:23 -04001466 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001467}
1468
Vladimir Lebedev72206232007-03-19 17:45:50 +03001469static int acpi_sbs_remove(struct acpi_device *device, int type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001470{
Len Browncece9012006-12-16 01:04:27 -05001471 struct acpi_sbs *sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001472 int id;
1473
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001474 if (!device) {
1475 return -EINVAL;
1476 }
1477
Vladimir Lebedev72206232007-03-19 17:45:50 +03001478 sbs = acpi_driver_data(device);
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001479 if (!sbs) {
Len Brown635227e2006-07-01 16:48:23 -04001480 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001481 }
1482
Vladimir Lebedev72206232007-03-19 17:45:50 +03001483 sbs_mutex_lock(sbs);
1484
Rich Townsend3f86b832006-07-01 11:36:54 -04001485 sbs->zombie = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001486 del_timer_sync(&sbs->update_timer);
1487 acpi_os_wait_events_complete(NULL);
1488 del_timer_sync(&sbs->update_timer);
1489
1490 for (id = 0; id < MAX_SBS_BAT; id++) {
1491 acpi_battery_remove(sbs, id);
1492 }
1493
1494 acpi_ac_remove(sbs);
1495
Vladimir Lebedev72206232007-03-19 17:45:50 +03001496 sbs_mutex_unlock(sbs);
1497
1498 mutex_destroy(&sbs->mutex);
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001499
Rich Townsend3f86b832006-07-01 11:36:54 -04001500 kfree(sbs);
1501
Len Brown635227e2006-07-01 16:48:23 -04001502 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001503}
1504
Vladimir Lebedev72206232007-03-19 17:45:50 +03001505static void acpi_sbs_rmdirs(void)
1506{
1507 if (acpi_ac_dir) {
1508 acpi_unlock_ac_dir(acpi_ac_dir);
1509 acpi_ac_dir = NULL;
1510 }
1511 if (acpi_battery_dir) {
1512 acpi_unlock_battery_dir(acpi_battery_dir);
1513 acpi_battery_dir = NULL;
1514 }
1515}
1516
1517static int acpi_sbs_resume(struct acpi_device *device)
1518{
1519 struct acpi_sbs *sbs;
1520
1521 if (!device)
1522 return -EINVAL;
1523
1524 sbs = device->driver_data;
1525
1526 sbs->run_cnt = 0;
1527
1528 return 0;
1529}
1530
Rich Townsend3f86b832006-07-01 11:36:54 -04001531static int __init acpi_sbs_init(void)
1532{
1533 int result = 0;
1534
Len Brownb20d2ae2006-08-15 23:21:37 -04001535 if (acpi_disabled)
1536 return -ENODEV;
1537
Rich Townsend3f86b832006-07-01 11:36:54 -04001538 if (capacity_mode != DEF_CAPACITY_UNIT
1539 && capacity_mode != MAH_CAPACITY_UNIT
1540 && capacity_mode != MWH_CAPACITY_UNIT) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001541 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev68451182007-03-19 17:45:50 +03001542 "invalid capacity_mode = %d", capacity_mode));
Len Brown635227e2006-07-01 16:48:23 -04001543 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001544 }
1545
1546 acpi_ac_dir = acpi_lock_ac_dir();
1547 if (!acpi_ac_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001548 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1549 "acpi_lock_ac_dir() failed"));
Len Brown635227e2006-07-01 16:48:23 -04001550 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001551 }
1552
1553 acpi_battery_dir = acpi_lock_battery_dir();
1554 if (!acpi_battery_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001555 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1556 "acpi_lock_battery_dir() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001557 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001558 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001559 }
1560
1561 result = acpi_bus_register_driver(&acpi_sbs_driver);
1562 if (result < 0) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001563 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1564 "acpi_bus_register_driver() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001565 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001566 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001567 }
1568
Len Brown635227e2006-07-01 16:48:23 -04001569 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001570}
1571
1572static void __exit acpi_sbs_exit(void)
1573{
Rich Townsend3f86b832006-07-01 11:36:54 -04001574 acpi_bus_unregister_driver(&acpi_sbs_driver);
1575
Vladimir Lebedev72206232007-03-19 17:45:50 +03001576 acpi_sbs_rmdirs();
Rich Townsend3f86b832006-07-01 11:36:54 -04001577
Len Brown635227e2006-07-01 16:48:23 -04001578 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001579}
1580
1581module_init(acpi_sbs_init);
1582module_exit(acpi_sbs_exit);