blob: f35fe63cef3d24b7c97c6df05ca9c452bcfe1926 [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
Alexey Starikovskiy89862e32007-09-26 19:43:35 +040091static unsigned int mode = CAPACITY_UNIT;
Rich Townsend3f86b832006-07-01 11:36:54 -040092
Vladimir Lebedev72206232007-03-19 17:45:50 +030093module_param(update_time, uint, 0644);
Alexey Starikovskiy89862e32007-09-26 19:43:35 +040094module_param(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
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400117struct acpi_battery {
118 struct acpi_sbs *sbs;
119 struct proc_dir_entry *proc_entry;
Rich Townsend3f86b832006-07-01 11:36:54 -0400120 int vscale;
121 int ipscale;
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400122 char manufacturer_name[ACPI_SBS_BLOCK_MAX];
123 char device_name[ACPI_SBS_BLOCK_MAX];
124 char device_chemistry[ACPI_SBS_BLOCK_MAX];
125 u16 full_charge_capacity;
126 u16 design_capacity;
127 u16 design_voltage;
128 u16 serial_number;
129 u16 voltage_now;
130 s16 current_now;
131 u16 capacity_now;
132 u16 state;
133 u16 alarm_capacity;
134 u16 mode;
135 u8 id;
136 u8 alive:1;
137 u8 init_state:1;
138 u8 present:1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400139};
140
141struct acpi_sbs {
Rich Townsend3f86b832006-07-01 11:36:54 -0400142 struct acpi_device *device;
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400143 struct acpi_smb_hc *hc;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300144 struct mutex mutex;
Rich Townsend3f86b832006-07-01 11:36:54 -0400145 struct proc_dir_entry *ac_entry;
146 struct acpi_battery battery[MAX_SBS_BAT];
Rich Townsend3f86b832006-07-01 11:36:54 -0400147 int zombie;
Rich Townsend3f86b832006-07-01 11:36:54 -0400148 struct timer_list update_timer;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300149 int run_cnt;
150 int update_proc_flg;
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400151 u8 batteries_supported;
152 u8 manager_present:1;
153 u8 charger_present:1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400154};
155
Vladimir Lebedev72206232007-03-19 17:45:50 +0300156static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type);
157static void acpi_sbs_update_time(void *data);
158
Vladimir Lebedev72206232007-03-19 17:45:50 +0300159static int sbs_zombie(struct acpi_sbs *sbs)
160{
161 return (sbs->zombie);
162}
163
164static int sbs_mutex_lock(struct acpi_sbs *sbs)
165{
166 if (sbs_zombie(sbs)) {
167 return -ENODEV;
168 }
169 mutex_lock(&sbs->mutex);
170 return 0;
171}
172
173static void sbs_mutex_unlock(struct acpi_sbs *sbs)
174{
175 mutex_unlock(&sbs->mutex);
176}
177
Rich Townsend3f86b832006-07-01 11:36:54 -0400178/* --------------------------------------------------------------------------
179 Smart Battery System Management
180 -------------------------------------------------------------------------- */
181
Vladimir Lebedev72206232007-03-19 17:45:50 +0300182static int acpi_check_update_proc(struct acpi_sbs *sbs)
183{
184 acpi_status status = AE_OK;
185
186 if (update_time == 0) {
187 sbs->update_proc_flg = 0;
188 return 0;
189 }
190 if (sbs->update_proc_flg == 0) {
191 status = acpi_os_execute(OSL_GPE_HANDLER,
192 acpi_sbs_update_time, sbs);
193 if (status != AE_OK) {
194 ACPI_EXCEPTION((AE_INFO, status,
195 "acpi_os_execute() failed"));
196 return 1;
197 }
198 sbs->update_proc_flg = 1;
199 }
200 return 0;
201}
Rich Townsend3f86b832006-07-01 11:36:54 -0400202
Rich Townsend3f86b832006-07-01 11:36:54 -0400203static int acpi_battery_get_present(struct acpi_battery *battery)
204{
205 s16 state;
206 int result = 0;
207 int is_present = 0;
208
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400209 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
210 ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400211 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300212 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400213 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400214 }
215 if (!result) {
216 is_present = (state & 0x000f) & (1 << battery->id);
217 }
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400218 battery->present = is_present;
Rich Townsend3f86b832006-07-01 11:36:54 -0400219
Len Brown635227e2006-07-01 16:48:23 -0400220 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400221}
222
Rich Townsend3f86b832006-07-01 11:36:54 -0400223static int acpi_battery_select(struct acpi_battery *battery)
224{
Vladimir Lebedev6d157022007-03-19 17:45:50 +0300225 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400226 int result = 0;
227 s16 state;
228 int foo;
229
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400230 if (sbs->manager_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400231
232 /* Take special care not to knobble other nibbles of
233 * state (aka selector_state), since
234 * it causes charging to halt on SBSELs */
235
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400236 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
237 ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400238 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300239 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400240 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400241 goto end;
242 }
243
244 foo = (state & 0x0fff) | (1 << (battery->id + 12));
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400245 result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD,
246 ACPI_SBSM_SMBUS_ADDR, 0x01, (u8 *)&foo, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400247 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300248 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400249 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400250 goto end;
251 }
252 }
253
254 end:
Len Brown635227e2006-07-01 16:48:23 -0400255 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400256}
257
258static int acpi_sbsm_get_info(struct acpi_sbs *sbs)
259{
Rich Townsend3f86b832006-07-01 11:36:54 -0400260 int result = 0;
261 s16 battery_system_info;
262
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400263 result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBSM_SMBUS_ADDR, 0x04,
264 (u8 *)&battery_system_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400265 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300266 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400267 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400268 goto end;
269 }
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400270 sbs->manager_present = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400271
272 end:
273
Len Brown635227e2006-07-01 16:48:23 -0400274 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400275}
276
277static int acpi_battery_get_info(struct acpi_battery *battery)
278{
Rich Townsend3f86b832006-07-01 11:36:54 -0400279 int result = 0;
280 s16 battery_mode;
281 s16 specification_info;
282
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400283 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
284 (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400285 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300286 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400287 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400288 goto end;
289 }
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400290 battery->mode = (battery_mode & 0x8000) >> 15;
Rich Townsend3f86b832006-07-01 11:36:54 -0400291
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400292 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x10,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400293 (u8 *)&battery->full_charge_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400294 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300295 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400296 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400297 goto end;
298 }
299
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400300 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x18,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400301 (u8 *)&battery->design_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400302
303 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300304 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400305 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400306 goto end;
307 }
308
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400309 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x19,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400310 (u8 *)&battery->design_voltage);
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, 0x1a,
318 (u8 *)&specification_info);
Rich Townsend3f86b832006-07-01 11:36:54 -0400319 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300320 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400321 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400322 goto end;
323 }
324
325 switch ((specification_info & 0x0f00) >> 8) {
326 case 1:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400327 battery->vscale = 10;
Rich Townsend3f86b832006-07-01 11:36:54 -0400328 break;
329 case 2:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400330 battery->vscale = 100;
Rich Townsend3f86b832006-07-01 11:36:54 -0400331 break;
332 case 3:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400333 battery->vscale = 1000;
Rich Townsend3f86b832006-07-01 11:36:54 -0400334 break;
335 default:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400336 battery->vscale = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400337 }
338
339 switch ((specification_info & 0xf000) >> 12) {
340 case 1:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400341 battery->ipscale = 10;
Rich Townsend3f86b832006-07-01 11:36:54 -0400342 break;
343 case 2:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400344 battery->ipscale = 100;
Rich Townsend3f86b832006-07-01 11:36:54 -0400345 break;
346 case 3:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400347 battery->ipscale = 1000;
Rich Townsend3f86b832006-07-01 11:36:54 -0400348 break;
349 default:
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400350 battery->ipscale = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -0400351 }
352
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400353 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x1c,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400354 (u8 *)&battery->serial_number);
Rich Townsend3f86b832006-07-01 11:36:54 -0400355 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300356 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400357 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400358 goto end;
359 }
360
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400361 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x20,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400362 (u8 *)battery->manufacturer_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400363 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300364 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
365 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400366 goto end;
367 }
368
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400369 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x21,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400370 (u8 *)battery->device_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400371 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300372 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
373 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400374 goto end;
375 }
376
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400377 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_BLOCK, ACPI_SB_SMBUS_ADDR, 0x22,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400378 (u8 *)battery->device_chemistry);
Rich Townsend3f86b832006-07-01 11:36:54 -0400379 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300380 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
381 "acpi_sbs_read_str() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400382 goto end;
383 }
384
385 end:
Len Brown635227e2006-07-01 16:48:23 -0400386 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400387}
388
Rich Townsend3f86b832006-07-01 11:36:54 -0400389static int acpi_battery_get_state(struct acpi_battery *battery)
390{
Rich Townsend3f86b832006-07-01 11:36:54 -0400391 int result = 0;
392
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400393 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x09,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400394 (u8 *)&battery->voltage_now);
Rich Townsend3f86b832006-07-01 11:36:54 -0400395 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300396 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400397 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400398 goto end;
399 }
400
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400401 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0a,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400402 (u8 *)&battery->current_now);
Rich Townsend3f86b832006-07-01 11:36:54 -0400403 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300404 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400405 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400406 goto end;
407 }
408
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400409 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x0f,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400410 (u8 *)&battery->capacity_now);
Rich Townsend3f86b832006-07-01 11:36:54 -0400411 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300412 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400413 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400414 goto end;
415 }
416
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400417 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x16,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400418 (u8 *)&battery->state);
Rich Townsend3f86b832006-07-01 11:36:54 -0400419 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300420 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400421 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400422 goto end;
423 }
424
Rich Townsend3f86b832006-07-01 11:36:54 -0400425 end:
Len Brown635227e2006-07-01 16:48:23 -0400426 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400427}
428
429static int acpi_battery_get_alarm(struct acpi_battery *battery)
430{
Rich Townsend3f86b832006-07-01 11:36:54 -0400431 int result = 0;
432
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400433 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400434 (u8 *)&battery->alarm_capacity);
Rich Townsend3f86b832006-07-01 11:36:54 -0400435 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300436 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400437 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400438 goto end;
439 }
440
Rich Townsend3f86b832006-07-01 11:36:54 -0400441 end:
442
Len Brown635227e2006-07-01 16:48:23 -0400443 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400444}
445
446static int acpi_battery_set_alarm(struct acpi_battery *battery,
447 unsigned long alarm)
448{
Rich Townsend3f86b832006-07-01 11:36:54 -0400449 int result = 0;
450 s16 battery_mode;
451 int foo;
452
Rich Townsend3f86b832006-07-01 11:36:54 -0400453 result = acpi_battery_select(battery);
454 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300455 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
456 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400457 goto end;
458 }
459
460 /* If necessary, enable the alarm */
461
462 if (alarm > 0) {
463 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400464 acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x03,
465 (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400466 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300467 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400468 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400469 goto end;
470 }
471
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400472 battery_mode &= 0xbfff;
Rich Townsend3f86b832006-07-01 11:36:54 -0400473 result =
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400474 acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
475 (u8 *)&battery_mode, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400476 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300477 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400478 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400479 goto end;
480 }
481 }
482
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400483 foo = alarm / (battery->mode ? 10 : 1);
484 result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SB_SMBUS_ADDR, 0x01,
485 (u8 *)&foo, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400486 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300487 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400488 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400489 goto end;
490 }
491
492 end:
493
Len Brown635227e2006-07-01 16:48:23 -0400494 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400495}
496
497static int acpi_battery_set_mode(struct acpi_battery *battery)
498{
499 int result = 0;
500 s16 battery_mode;
501
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400502 if (mode == DEF_CAPACITY_UNIT) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400503 goto end;
504 }
505
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400506 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400507 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400508 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300509 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400510 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400511 goto end;
512 }
513
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400514 if (mode == MAH_CAPACITY_UNIT) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400515 battery_mode &= 0x7fff;
516 } else {
517 battery_mode |= 0x8000;
518 }
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400519 result = acpi_smbus_write(battery->sbs->hc, SMBUS_READ_WORD,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400520 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode, 2);
Rich Townsend3f86b832006-07-01 11:36:54 -0400521 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300522 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400523 "acpi_smbus_write() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400524 goto end;
525 }
526
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400527 result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400528 ACPI_SB_SMBUS_ADDR, 0x03, (u8 *)&battery_mode);
Rich Townsend3f86b832006-07-01 11:36:54 -0400529 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300530 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400531 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400532 goto end;
533 }
534
535 end:
Len Brown635227e2006-07-01 16:48:23 -0400536 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400537}
538
539static int acpi_battery_init(struct acpi_battery *battery)
540{
541 int result = 0;
542
Rich Townsend3f86b832006-07-01 11:36:54 -0400543 result = acpi_battery_select(battery);
544 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300545 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +0300546 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400547 goto end;
548 }
549
550 result = acpi_battery_set_mode(battery);
551 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300552 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
553 "acpi_battery_set_mode() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400554 goto end;
555 }
556
557 result = acpi_battery_get_info(battery);
558 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300559 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
560 "acpi_battery_get_info() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400561 goto end;
562 }
563
564 result = acpi_battery_get_state(battery);
565 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300566 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
567 "acpi_battery_get_state() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400568 goto end;
569 }
570
571 result = acpi_battery_get_alarm(battery);
572 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300573 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
574 "acpi_battery_get_alarm() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400575 goto end;
576 }
577
578 end:
Len Brown635227e2006-07-01 16:48:23 -0400579 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400580}
581
582static int acpi_ac_get_present(struct acpi_sbs *sbs)
583{
Rich Townsend3f86b832006-07-01 11:36:54 -0400584 int result = 0;
585 s16 charger_status;
586
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400587 result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBC_SMBUS_ADDR, 0x13,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400588 (u8 *)&charger_status);
Rich Townsend3f86b832006-07-01 11:36:54 -0400589
590 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300591 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy91087df2007-09-26 19:43:28 +0400592 "acpi_smbus_read() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400593 goto end;
594 }
595
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400596 sbs->charger_present = (charger_status & 0x8000) >> 15;
Rich Townsend3f86b832006-07-01 11:36:54 -0400597
598 end:
599
Len Brown635227e2006-07-01 16:48:23 -0400600 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400601}
602
603/* --------------------------------------------------------------------------
604 FS Interface (/proc/acpi)
605 -------------------------------------------------------------------------- */
606
607/* Generic Routines */
608
609static int
610acpi_sbs_generic_add_fs(struct proc_dir_entry **dir,
611 struct proc_dir_entry *parent_dir,
612 char *dir_name,
613 struct file_operations *info_fops,
614 struct file_operations *state_fops,
615 struct file_operations *alarm_fops, void *data)
616{
617 struct proc_dir_entry *entry = NULL;
618
Rich Townsend3f86b832006-07-01 11:36:54 -0400619 if (!*dir) {
620 *dir = proc_mkdir(dir_name, parent_dir);
621 if (!*dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300622 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
623 "proc_mkdir() failed"));
Len Brown635227e2006-07-01 16:48:23 -0400624 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400625 }
626 (*dir)->owner = THIS_MODULE;
627 }
628
629 /* 'info' [R] */
630 if (info_fops) {
631 entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir);
632 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300633 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
634 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400635 } else {
636 entry->proc_fops = info_fops;
637 entry->data = data;
638 entry->owner = THIS_MODULE;
639 }
640 }
641
642 /* 'state' [R] */
643 if (state_fops) {
644 entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir);
645 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300646 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
647 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400648 } else {
649 entry->proc_fops = state_fops;
650 entry->data = data;
651 entry->owner = THIS_MODULE;
652 }
653 }
654
655 /* 'alarm' [R/W] */
656 if (alarm_fops) {
657 entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir);
658 if (!entry) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300659 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
660 "create_proc_entry() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400661 } else {
662 entry->proc_fops = alarm_fops;
663 entry->data = data;
664 entry->owner = THIS_MODULE;
665 }
666 }
667
Len Brown635227e2006-07-01 16:48:23 -0400668 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -0400669}
670
671static void
672acpi_sbs_generic_remove_fs(struct proc_dir_entry **dir,
673 struct proc_dir_entry *parent_dir)
674{
Rich Townsend3f86b832006-07-01 11:36:54 -0400675
676 if (*dir) {
677 remove_proc_entry(ACPI_SBS_FILE_INFO, *dir);
678 remove_proc_entry(ACPI_SBS_FILE_STATE, *dir);
679 remove_proc_entry(ACPI_SBS_FILE_ALARM, *dir);
680 remove_proc_entry((*dir)->name, parent_dir);
681 *dir = NULL;
682 }
683
684}
685
686/* Smart Battery Interface */
687
688static struct proc_dir_entry *acpi_battery_dir = NULL;
689
690static int acpi_battery_read_info(struct seq_file *seq, void *offset)
691{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200692 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300693 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400694 int cscale;
695 int result = 0;
696
Vladimir Lebedev72206232007-03-19 17:45:50 +0300697 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400698 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400699 }
700
Vladimir Lebedev72206232007-03-19 17:45:50 +0300701 result = acpi_check_update_proc(sbs);
702 if (result)
703 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400704
Vladimir Lebedev72206232007-03-19 17:45:50 +0300705 if (update_time == 0) {
706 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO);
Rich Townsend3f86b832006-07-01 11:36:54 -0400707 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300708 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
709 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400710 }
711 }
712
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400713 if (battery->present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400714 seq_printf(seq, "present: yes\n");
715 } else {
716 seq_printf(seq, "present: no\n");
717 goto end;
718 }
719
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400720 if (battery->mode) {
721 cscale = battery->vscale * battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400722 } else {
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400723 cscale = battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400724 }
Vladimir Lebedev72206232007-03-19 17:45:50 +0300725 seq_printf(seq, "design capacity: %i%s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400726 battery->design_capacity * cscale,
727 battery->mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400728
Vladimir Lebedev72206232007-03-19 17:45:50 +0300729 seq_printf(seq, "last full capacity: %i%s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400730 battery->full_charge_capacity * cscale,
731 battery->mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400732
733 seq_printf(seq, "battery technology: rechargeable\n");
734
735 seq_printf(seq, "design voltage: %i mV\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400736 battery->design_voltage * battery->vscale);
Rich Townsend3f86b832006-07-01 11:36:54 -0400737
738 seq_printf(seq, "design capacity warning: unknown\n");
739 seq_printf(seq, "design capacity low: unknown\n");
740 seq_printf(seq, "capacity granularity 1: unknown\n");
741 seq_printf(seq, "capacity granularity 2: unknown\n");
742
743 seq_printf(seq, "model number: %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400744 battery->device_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400745
746 seq_printf(seq, "serial number: %i\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400747 battery->serial_number);
Rich Townsend3f86b832006-07-01 11:36:54 -0400748
749 seq_printf(seq, "battery type: %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400750 battery->device_chemistry);
Rich Townsend3f86b832006-07-01 11:36:54 -0400751
752 seq_printf(seq, "OEM info: %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400753 battery->manufacturer_name);
Rich Townsend3f86b832006-07-01 11:36:54 -0400754
755 end:
756
Vladimir Lebedev72206232007-03-19 17:45:50 +0300757 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400758
Len Brown635227e2006-07-01 16:48:23 -0400759 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400760}
761
762static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
763{
764 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
765}
766
767static int acpi_battery_read_state(struct seq_file *seq, void *offset)
768{
Vladimir Lebedev72206232007-03-19 17:45:50 +0300769 struct acpi_battery *battery = seq->private;
770 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400771 int result = 0;
772 int cscale;
773 int foo;
774
Vladimir Lebedev72206232007-03-19 17:45:50 +0300775 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400776 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400777 }
778
Vladimir Lebedev72206232007-03-19 17:45:50 +0300779 result = acpi_check_update_proc(sbs);
780 if (result)
781 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400782
Vladimir Lebedev72206232007-03-19 17:45:50 +0300783 if (update_time == 0) {
784 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -0400785 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300786 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
787 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400788 }
789 }
790
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400791 if (battery->present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400792 seq_printf(seq, "present: yes\n");
793 } else {
794 seq_printf(seq, "present: no\n");
795 goto end;
796 }
797
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400798 if (battery->mode) {
799 cscale = battery->vscale * battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400800 } else {
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400801 cscale = battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400802 }
803
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400804 if (battery->state & 0x0010) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400805 seq_printf(seq, "capacity state: critical\n");
806 } else {
807 seq_printf(seq, "capacity state: ok\n");
808 }
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500809
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400810 foo = (s16) battery->current_now * battery->ipscale;
811 if (battery->mode) {
812 foo = foo * battery->design_voltage / 1000;
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500813 }
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400814 if (battery->current_now < 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400815 seq_printf(seq, "charging state: discharging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500816 seq_printf(seq, "present rate: %d %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400817 -foo, battery->mode ? "mW" : "mA");
818 } else if (battery->current_now > 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400819 seq_printf(seq, "charging state: charging\n");
Vladimir Lebedeve6d0f562007-02-10 01:51:13 -0500820 seq_printf(seq, "present rate: %d %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400821 foo, battery->mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -0400822 } else {
823 seq_printf(seq, "charging state: charged\n");
824 seq_printf(seq, "present rate: 0 %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400825 battery->mode ? "mW" : "mA");
Rich Townsend3f86b832006-07-01 11:36:54 -0400826 }
827
Vladimir Lebedev72206232007-03-19 17:45:50 +0300828 seq_printf(seq, "remaining capacity: %i%s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400829 battery->capacity_now * cscale,
830 battery->mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400831
832 seq_printf(seq, "present voltage: %i mV\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400833 battery->voltage_now * battery->vscale);
Rich Townsend3f86b832006-07-01 11:36:54 -0400834
835 end:
836
Vladimir Lebedev72206232007-03-19 17:45:50 +0300837 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400838
Len Brown635227e2006-07-01 16:48:23 -0400839 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400840}
841
842static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
843{
844 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
845}
846
847static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
848{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200849 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300850 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400851 int result = 0;
852 int cscale;
853
Vladimir Lebedev72206232007-03-19 17:45:50 +0300854 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400855 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400856 }
857
Vladimir Lebedev72206232007-03-19 17:45:50 +0300858 result = acpi_check_update_proc(sbs);
859 if (result)
860 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400861
Vladimir Lebedev72206232007-03-19 17:45:50 +0300862 if (update_time == 0) {
863 result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM);
Rich Townsend3f86b832006-07-01 11:36:54 -0400864 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300865 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
866 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -0400867 }
868 }
869
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400870 if (!battery->present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400871 seq_printf(seq, "present: no\n");
872 goto end;
873 }
874
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400875 if (battery->mode) {
876 cscale = battery->vscale * battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400877 } else {
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400878 cscale = battery->ipscale;
Rich Townsend3f86b832006-07-01 11:36:54 -0400879 }
880
881 seq_printf(seq, "alarm: ");
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400882 if (battery->alarm_capacity) {
Vladimir Lebedev72206232007-03-19 17:45:50 +0300883 seq_printf(seq, "%i%s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400884 battery->alarm_capacity * cscale,
885 battery->mode ? "0 mWh" : " mAh");
Rich Townsend3f86b832006-07-01 11:36:54 -0400886 } else {
887 seq_printf(seq, "disabled\n");
888 }
889
890 end:
891
Vladimir Lebedev72206232007-03-19 17:45:50 +0300892 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400893
Len Brown635227e2006-07-01 16:48:23 -0400894 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400895}
896
897static ssize_t
898acpi_battery_write_alarm(struct file *file, const char __user * buffer,
899 size_t count, loff_t * ppos)
900{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200901 struct seq_file *seq = file->private_data;
902 struct acpi_battery *battery = seq->private;
Vladimir Lebedev72206232007-03-19 17:45:50 +0300903 struct acpi_sbs *sbs = battery->sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -0400904 char alarm_string[12] = { '\0' };
905 int result, old_alarm, new_alarm;
906
Vladimir Lebedev72206232007-03-19 17:45:50 +0300907 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -0400908 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -0400909 }
910
Vladimir Lebedev72206232007-03-19 17:45:50 +0300911 result = acpi_check_update_proc(sbs);
912 if (result)
913 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -0400914
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400915 if (!battery->present) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400916 result = -ENODEV;
917 goto end;
918 }
919
920 if (count > sizeof(alarm_string) - 1) {
921 result = -EINVAL;
922 goto end;
923 }
924
925 if (copy_from_user(alarm_string, buffer, count)) {
926 result = -EFAULT;
927 goto end;
928 }
929
930 alarm_string[count] = 0;
931
Alexey Starikovskiy89862e32007-09-26 19:43:35 +0400932 old_alarm = battery->alarm_capacity;
Rich Townsend3f86b832006-07-01 11:36:54 -0400933 new_alarm = simple_strtoul(alarm_string, NULL, 0);
934
935 result = acpi_battery_set_alarm(battery, new_alarm);
936 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300937 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
938 "acpi_battery_set_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200939 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -0400940 goto end;
941 }
942 result = acpi_battery_get_alarm(battery);
943 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +0300944 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
945 "acpi_battery_get_alarm() failed"));
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200946 acpi_battery_set_alarm(battery, old_alarm);
Rich Townsend3f86b832006-07-01 11:36:54 -0400947 goto end;
948 }
949
950 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +0300951 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -0400952
953 if (result) {
Len Brown635227e2006-07-01 16:48:23 -0400954 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -0400955 } else {
Len Brown635227e2006-07-01 16:48:23 -0400956 return count;
Rich Townsend3f86b832006-07-01 11:36:54 -0400957 }
958}
959
960static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
961{
962 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
963}
964
965static struct file_operations acpi_battery_info_fops = {
966 .open = acpi_battery_info_open_fs,
967 .read = seq_read,
968 .llseek = seq_lseek,
969 .release = single_release,
970 .owner = THIS_MODULE,
971};
972
973static struct file_operations acpi_battery_state_fops = {
974 .open = acpi_battery_state_open_fs,
975 .read = seq_read,
976 .llseek = seq_lseek,
977 .release = single_release,
978 .owner = THIS_MODULE,
979};
980
981static struct file_operations acpi_battery_alarm_fops = {
982 .open = acpi_battery_alarm_open_fs,
983 .read = seq_read,
984 .write = acpi_battery_write_alarm,
985 .llseek = seq_lseek,
986 .release = single_release,
987 .owner = THIS_MODULE,
988};
989
990/* Legacy AC Adapter Interface */
991
992static struct proc_dir_entry *acpi_ac_dir = NULL;
993
994static int acpi_ac_read_state(struct seq_file *seq, void *offset)
995{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200996 struct acpi_sbs *sbs = seq->private;
Rich Townsend3f86b832006-07-01 11:36:54 -0400997 int result;
998
Vladimir Lebedev72206232007-03-19 17:45:50 +0300999 if (sbs_mutex_lock(sbs)) {
Len Brown635227e2006-07-01 16:48:23 -04001000 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001001 }
1002
Vladimir Lebedev72206232007-03-19 17:45:50 +03001003 if (update_time == 0) {
1004 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE);
Rich Townsend3f86b832006-07-01 11:36:54 -04001005 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001006 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1007 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001008 }
1009 }
1010
1011 seq_printf(seq, "state: %s\n",
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001012 sbs->charger_present ? "on-line" : "off-line");
Rich Townsend3f86b832006-07-01 11:36:54 -04001013
Vladimir Lebedev72206232007-03-19 17:45:50 +03001014 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001015
Len Brown635227e2006-07-01 16:48:23 -04001016 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001017}
1018
1019static int acpi_ac_state_open_fs(struct inode *inode, struct file *file)
1020{
1021 return single_open(file, acpi_ac_read_state, PDE(inode)->data);
1022}
1023
1024static struct file_operations acpi_ac_state_fops = {
1025 .open = acpi_ac_state_open_fs,
1026 .read = seq_read,
1027 .llseek = seq_lseek,
1028 .release = single_release,
1029 .owner = THIS_MODULE,
1030};
1031
1032/* --------------------------------------------------------------------------
1033 Driver Interface
1034 -------------------------------------------------------------------------- */
1035
1036/* Smart Battery */
1037
1038static int acpi_battery_add(struct acpi_sbs *sbs, int id)
1039{
1040 int is_present;
1041 int result;
1042 char dir_name[32];
1043 struct acpi_battery *battery;
1044
Rich Townsend3f86b832006-07-01 11:36:54 -04001045 battery = &sbs->battery[id];
1046
1047 battery->alive = 0;
1048
1049 battery->init_state = 0;
1050 battery->id = id;
1051 battery->sbs = sbs;
1052
1053 result = acpi_battery_select(battery);
1054 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001055 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1056 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001057 goto end;
1058 }
1059
1060 result = acpi_battery_get_present(battery);
1061 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001062 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1063 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001064 goto end;
1065 }
1066
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001067 is_present = battery->present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001068
1069 if (is_present) {
1070 result = acpi_battery_init(battery);
1071 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001072 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1073 "acpi_battery_init() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001074 goto end;
1075 }
1076 battery->init_state = 1;
1077 }
1078
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001079 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Rich Townsend3f86b832006-07-01 11:36:54 -04001080
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001081 result = acpi_sbs_generic_add_fs(&battery->proc_entry,
Rich Townsend3f86b832006-07-01 11:36:54 -04001082 acpi_battery_dir,
1083 dir_name,
1084 &acpi_battery_info_fops,
1085 &acpi_battery_state_fops,
1086 &acpi_battery_alarm_fops, battery);
1087 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001088 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1089 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001090 goto end;
1091 }
1092 battery->alive = 1;
1093
Vladimir Lebedev72206232007-03-19 17:45:50 +03001094 printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n",
1095 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001096 sbs->battery->present ? "present" : "absent");
Vladimir Lebedev72206232007-03-19 17:45:50 +03001097
Rich Townsend3f86b832006-07-01 11:36:54 -04001098 end:
Len Brown635227e2006-07-01 16:48:23 -04001099 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001100}
1101
1102static void acpi_battery_remove(struct acpi_sbs *sbs, int id)
1103{
Rich Townsend3f86b832006-07-01 11:36:54 -04001104
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001105 if (sbs->battery[id].proc_entry) {
1106 acpi_sbs_generic_remove_fs(&(sbs->battery[id].proc_entry),
Rich Townsend3f86b832006-07-01 11:36:54 -04001107 acpi_battery_dir);
1108 }
1109}
1110
1111static int acpi_ac_add(struct acpi_sbs *sbs)
1112{
1113 int result;
1114
Rich Townsend3f86b832006-07-01 11:36:54 -04001115 result = acpi_ac_get_present(sbs);
1116 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001117 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1118 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001119 goto end;
1120 }
1121
1122 result = acpi_sbs_generic_add_fs(&sbs->ac_entry,
1123 acpi_ac_dir,
1124 ACPI_AC_DIR_NAME,
1125 NULL, &acpi_ac_state_fops, NULL, sbs);
1126 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001127 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1128 "acpi_sbs_generic_add_fs() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001129 goto end;
1130 }
1131
Vladimir Lebedev72206232007-03-19 17:45:50 +03001132 printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n",
1133 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001134 ACPI_AC_DIR_NAME, sbs->charger_present ? "on-line" : "off-line");
Vladimir Lebedev72206232007-03-19 17:45:50 +03001135
Rich Townsend3f86b832006-07-01 11:36:54 -04001136 end:
1137
Len Brown635227e2006-07-01 16:48:23 -04001138 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001139}
1140
1141static void acpi_ac_remove(struct acpi_sbs *sbs)
1142{
Rich Townsend3f86b832006-07-01 11:36:54 -04001143
1144 if (sbs->ac_entry) {
1145 acpi_sbs_generic_remove_fs(&sbs->ac_entry, acpi_ac_dir);
1146 }
1147}
1148
Vladimir Lebedev72206232007-03-19 17:45:50 +03001149static void acpi_sbs_update_time_run(unsigned long data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001150{
Vladimir Lebedev72206232007-03-19 17:45:50 +03001151 acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data);
Rich Townsend3f86b832006-07-01 11:36:54 -04001152}
1153
Vladimir Lebedev72206232007-03-19 17:45:50 +03001154static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001155{
1156 struct acpi_battery *battery;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001157 int result = 0, cnt;
1158 int old_ac_present = -1;
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001159 int old_present = -1;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001160 int new_ac_present = -1;
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001161 int new_present = -1;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001162 int id_min = 0, id_max = MAX_SBS_BAT - 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001163 char dir_name[32];
Vladimir Lebedev72206232007-03-19 17:45:50 +03001164 int do_battery_init = 0, do_ac_init = 0;
1165 int old_remaining_capacity = 0;
Adrian Bunkbc90a012007-07-09 11:33:15 -07001166 int update_battery = 1;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001167 int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001168
Vladimir Lebedev72206232007-03-19 17:45:50 +03001169 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001170 goto end;
1171 }
1172
Vladimir Lebedev72206232007-03-19 17:45:50 +03001173 if (id >= 0) {
1174 id_min = id_max = id;
1175 }
1176
1177 if (data_type == DATA_TYPE_COMMON && up_tm > 0) {
1178 cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1179 if (sbs->run_cnt % cnt != 0) {
1180 update_battery = 0;
1181 }
1182 }
1183
1184 sbs->run_cnt++;
1185
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001186 if (!update_battery) {
1187 goto end;
1188 }
1189
1190 old_ac_present = sbs->charger_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001191
1192 result = acpi_ac_get_present(sbs);
1193 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001194 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1195 "acpi_ac_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001196 }
1197
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001198 new_ac_present = sbs->charger_present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001199
1200 do_ac_init = (old_ac_present != new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001201 if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) {
1202 do_ac_init = 1;
1203 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001204
Vladimir Lebedev72206232007-03-19 17:45:50 +03001205 if (do_ac_init) {
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001206 result = acpi_bus_generate_proc_event4(ACPI_AC_CLASS,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001207 ACPI_AC_DIR_NAME,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001208 ACPI_SBS_AC_NOTIFY_STATUS,
1209 new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001210 if (result) {
1211 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001212 "acpi_bus_generate_event4() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001213 }
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001214 acpi_bus_generate_netlink_event(ACPI_AC_CLASS, ACPI_AC_DIR_NAME,
1215 ACPI_SBS_AC_NOTIFY_STATUS,
1216 new_ac_present);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001217 }
1218
1219 if (data_type == DATA_TYPE_COMMON) {
1220 if (!do_ac_init && !update_battery) {
1221 goto end;
1222 }
1223 }
1224
1225 if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001226 goto end;
1227 }
1228
Vladimir Lebedev72206232007-03-19 17:45:50 +03001229 for (id = id_min; id <= id_max; id++) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001230 battery = &sbs->battery[id];
1231 if (battery->alive == 0) {
1232 continue;
1233 }
1234
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001235 old_remaining_capacity = battery->capacity_now;
Rich Townsend3f86b832006-07-01 11:36:54 -04001236
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001237 old_present = battery->present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001238
1239 result = acpi_battery_select(battery);
1240 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001241 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1242 "acpi_battery_select() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001243 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001244
1245 result = acpi_battery_get_present(battery);
1246 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001247 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1248 "acpi_battery_get_present() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001249 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001250
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001251 new_present = battery->present;
Rich Townsend3f86b832006-07-01 11:36:54 -04001252
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001253 do_battery_init = ((old_present != new_present)
1254 && new_present);
1255 if (!new_present)
Vladimir Lebedev72206232007-03-19 17:45:50 +03001256 goto event;
1257 if (do_ac_init || do_battery_init) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001258 result = acpi_battery_init(battery);
1259 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001260 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1261 "acpi_battery_init() "
1262 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001263 }
1264 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001265 if (sbs_zombie(sbs)) {
1266 goto end;
1267 }
1268
1269 if ((data_type == DATA_TYPE_COMMON
1270 || data_type == DATA_TYPE_INFO)
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001271 && new_present) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001272 result = acpi_battery_get_info(battery);
1273 if (result) {
1274 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1275 "acpi_battery_get_info() failed"));
1276 }
1277 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001278 if (data_type == DATA_TYPE_INFO) {
1279 continue;
1280 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001281 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001282 goto end;
1283 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001284
1285 if ((data_type == DATA_TYPE_COMMON
1286 || data_type == DATA_TYPE_STATE)
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001287 && new_present) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001288 result = acpi_battery_get_state(battery);
1289 if (result) {
1290 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1291 "acpi_battery_get_state() failed"));
1292 }
1293 }
1294 if (data_type == DATA_TYPE_STATE) {
1295 goto event;
1296 }
1297 if (sbs_zombie(sbs)) {
1298 goto end;
1299 }
1300
1301 if ((data_type == DATA_TYPE_COMMON
1302 || data_type == DATA_TYPE_ALARM)
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001303 && new_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001304 result = acpi_battery_get_alarm(battery);
1305 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001306 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1307 "acpi_battery_get_alarm() "
1308 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001309 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001310 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001311 if (data_type == DATA_TYPE_ALARM) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001312 continue;
1313 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001314 if (sbs_zombie(sbs)) {
1315 goto end;
1316 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001317
Vladimir Lebedev72206232007-03-19 17:45:50 +03001318 event:
1319
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001320 if (old_present != new_present || do_ac_init ||
Vladimir Lebedev72206232007-03-19 17:45:50 +03001321 old_remaining_capacity !=
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001322 battery->capacity_now) {
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001323 sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id);
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001324 result = acpi_bus_generate_proc_event4(ACPI_BATTERY_CLASS,
Rich Townsend3f86b832006-07-01 11:36:54 -04001325 dir_name,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001326 ACPI_SBS_BATTERY_NOTIFY_STATUS,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001327 new_present);
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001328 acpi_bus_generate_netlink_event(ACPI_BATTERY_CLASS, dir_name,
1329 ACPI_SBS_BATTERY_NOTIFY_STATUS,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001330 new_present);
Rich Townsend3f86b832006-07-01 11:36:54 -04001331 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001332 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy8db85d42007-09-26 19:43:16 +04001333 "acpi_bus_generate_proc_event4() "
Vladimir Lebedev68451182007-03-19 17:45:50 +03001334 "failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001335 }
1336 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001337 }
1338
1339 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001340
Len Brown635227e2006-07-01 16:48:23 -04001341 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001342}
1343
Vladimir Lebedev72206232007-03-19 17:45:50 +03001344static void acpi_sbs_update_time(void *data)
Rich Townsend3f86b832006-07-01 11:36:54 -04001345{
1346 struct acpi_sbs *sbs = data;
1347 unsigned long delay = -1;
1348 int result;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001349 unsigned int up_tm = update_time;
Rich Townsend3f86b832006-07-01 11:36:54 -04001350
Vladimir Lebedev72206232007-03-19 17:45:50 +03001351 if (sbs_mutex_lock(sbs))
1352 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001353
Vladimir Lebedev72206232007-03-19 17:45:50 +03001354 result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON);
Rich Townsend3f86b832006-07-01 11:36:54 -04001355 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001356 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1357 "acpi_sbs_update_run() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001358 }
1359
Vladimir Lebedev72206232007-03-19 17:45:50 +03001360 if (sbs_zombie(sbs)) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001361 goto end;
1362 }
1363
Vladimir Lebedev72206232007-03-19 17:45:50 +03001364 if (!up_tm) {
1365 if (timer_pending(&sbs->update_timer))
1366 del_timer(&sbs->update_timer);
1367 } else {
1368 delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm);
1369 delay = jiffies + HZ * delay;
1370 if (timer_pending(&sbs->update_timer)) {
1371 mod_timer(&sbs->update_timer, delay);
1372 } else {
1373 sbs->update_timer.data = (unsigned long)data;
1374 sbs->update_timer.function = acpi_sbs_update_time_run;
1375 sbs->update_timer.expires = delay;
1376 add_timer(&sbs->update_timer);
1377 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001378 }
1379
Rich Townsend3f86b832006-07-01 11:36:54 -04001380 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001381
1382 sbs_mutex_unlock(sbs);
Rich Townsend3f86b832006-07-01 11:36:54 -04001383}
1384
1385static int acpi_sbs_add(struct acpi_device *device)
1386{
1387 struct acpi_sbs *sbs = NULL;
Vladimir Lebedev72206232007-03-19 17:45:50 +03001388 int result = 0, remove_result = 0;
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001389 int id;
Rich Townsend3f86b832006-07-01 11:36:54 -04001390
Burman Yan36bcbec2006-12-19 12:56:11 -08001391 sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
Rich Townsend3f86b832006-07-01 11:36:54 -04001392 if (!sbs) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001393 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed"));
1394 result = -ENOMEM;
1395 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001396 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001397
Vladimir Lebedev72206232007-03-19 17:45:50 +03001398 mutex_init(&sbs->mutex);
1399
1400 sbs_mutex_lock(sbs);
1401
Rich Townsend3f86b832006-07-01 11:36:54 -04001402 sbs->device = device;
Alexey Starikovskiy91087df2007-09-26 19:43:28 +04001403 sbs->hc = acpi_driver_data(device->parent);
Rich Townsend3f86b832006-07-01 11:36:54 -04001404
1405 strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME);
1406 strcpy(acpi_device_class(device), ACPI_SBS_CLASS);
1407 acpi_driver_data(device) = sbs;
1408
Rich Townsend3f86b832006-07-01 11:36:54 -04001409 result = acpi_ac_add(sbs);
1410 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001411 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001412 goto end;
1413 }
Vladimir Lebedev72206232007-03-19 17:45:50 +03001414
Alexey Starikovskiyaddad452007-06-23 16:24:48 -04001415 acpi_sbsm_get_info(sbs);
1416
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001417 if (!sbs->manager_present) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001418 result = acpi_battery_add(sbs, 0);
1419 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001420 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1421 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001422 goto end;
1423 }
1424 } else {
1425 for (id = 0; id < MAX_SBS_BAT; id++) {
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001426 if ((sbs->batteries_supported & (1 << id))) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001427 result = acpi_battery_add(sbs, id);
1428 if (result) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001429 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Vladimir Lebedev72206232007-03-19 17:45:50 +03001430 "acpi_battery_add() failed"));
Rich Townsend3f86b832006-07-01 11:36:54 -04001431 goto end;
1432 }
1433 }
1434 }
1435 }
1436
Rich Townsend3f86b832006-07-01 11:36:54 -04001437 init_timer(&sbs->update_timer);
Vladimir Lebedev72206232007-03-19 17:45:50 +03001438 result = acpi_check_update_proc(sbs);
1439 if (result)
1440 goto end;
Rich Townsend3f86b832006-07-01 11:36:54 -04001441
1442 end:
Vladimir Lebedev72206232007-03-19 17:45:50 +03001443
1444 sbs_mutex_unlock(sbs);
1445
Rich Townsend3f86b832006-07-01 11:36:54 -04001446 if (result) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001447 remove_result = acpi_sbs_remove(device, 0);
1448 if (remove_result) {
1449 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1450 "acpi_sbs_remove() failed"));
1451 }
Rich Townsend3f86b832006-07-01 11:36:54 -04001452 }
1453
Len Brown635227e2006-07-01 16:48:23 -04001454 return result;
Rich Townsend3f86b832006-07-01 11:36:54 -04001455}
1456
Vladimir Lebedev72206232007-03-19 17:45:50 +03001457static int acpi_sbs_remove(struct acpi_device *device, int type)
Rich Townsend3f86b832006-07-01 11:36:54 -04001458{
Len Browncece9012006-12-16 01:04:27 -05001459 struct acpi_sbs *sbs;
Rich Townsend3f86b832006-07-01 11:36:54 -04001460 int id;
1461
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001462 if (!device) {
1463 return -EINVAL;
1464 }
1465
Vladimir Lebedev72206232007-03-19 17:45:50 +03001466 sbs = acpi_driver_data(device);
Lebedev, Vladimir P963497c2006-09-05 19:49:13 +04001467 if (!sbs) {
Len Brown635227e2006-07-01 16:48:23 -04001468 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001469 }
1470
Vladimir Lebedev72206232007-03-19 17:45:50 +03001471 sbs_mutex_lock(sbs);
1472
Rich Townsend3f86b832006-07-01 11:36:54 -04001473 sbs->zombie = 1;
Rich Townsend3f86b832006-07-01 11:36:54 -04001474 del_timer_sync(&sbs->update_timer);
1475 acpi_os_wait_events_complete(NULL);
1476 del_timer_sync(&sbs->update_timer);
1477
1478 for (id = 0; id < MAX_SBS_BAT; id++) {
1479 acpi_battery_remove(sbs, id);
1480 }
1481
1482 acpi_ac_remove(sbs);
1483
Vladimir Lebedev72206232007-03-19 17:45:50 +03001484 sbs_mutex_unlock(sbs);
1485
1486 mutex_destroy(&sbs->mutex);
Vladimir Lebedev6d157022007-03-19 17:45:50 +03001487
Rich Townsend3f86b832006-07-01 11:36:54 -04001488 kfree(sbs);
1489
Len Brown635227e2006-07-01 16:48:23 -04001490 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001491}
1492
Vladimir Lebedev72206232007-03-19 17:45:50 +03001493static void acpi_sbs_rmdirs(void)
1494{
1495 if (acpi_ac_dir) {
1496 acpi_unlock_ac_dir(acpi_ac_dir);
1497 acpi_ac_dir = NULL;
1498 }
1499 if (acpi_battery_dir) {
1500 acpi_unlock_battery_dir(acpi_battery_dir);
1501 acpi_battery_dir = NULL;
1502 }
1503}
1504
1505static int acpi_sbs_resume(struct acpi_device *device)
1506{
1507 struct acpi_sbs *sbs;
1508
1509 if (!device)
1510 return -EINVAL;
1511
1512 sbs = device->driver_data;
1513
1514 sbs->run_cnt = 0;
1515
1516 return 0;
1517}
1518
Rich Townsend3f86b832006-07-01 11:36:54 -04001519static int __init acpi_sbs_init(void)
1520{
1521 int result = 0;
1522
Len Brownb20d2ae2006-08-15 23:21:37 -04001523 if (acpi_disabled)
1524 return -ENODEV;
1525
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001526 if (mode != DEF_CAPACITY_UNIT
1527 && mode != MAH_CAPACITY_UNIT
1528 && mode != MWH_CAPACITY_UNIT) {
Vladimir Lebedev72206232007-03-19 17:45:50 +03001529 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
Alexey Starikovskiy89862e32007-09-26 19:43:35 +04001530 "invalid mode = %d", mode));
Len Brown635227e2006-07-01 16:48:23 -04001531 return -EINVAL;
Rich Townsend3f86b832006-07-01 11:36:54 -04001532 }
1533
1534 acpi_ac_dir = acpi_lock_ac_dir();
1535 if (!acpi_ac_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001536 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1537 "acpi_lock_ac_dir() failed"));
Len Brown635227e2006-07-01 16:48:23 -04001538 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001539 }
1540
1541 acpi_battery_dir = acpi_lock_battery_dir();
1542 if (!acpi_battery_dir) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001543 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1544 "acpi_lock_battery_dir() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001545 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001546 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001547 }
1548
1549 result = acpi_bus_register_driver(&acpi_sbs_driver);
1550 if (result < 0) {
Vladimir Lebedev68451182007-03-19 17:45:50 +03001551 ACPI_EXCEPTION((AE_INFO, AE_ERROR,
1552 "acpi_bus_register_driver() failed"));
Vladimir Lebedev72206232007-03-19 17:45:50 +03001553 acpi_sbs_rmdirs();
Len Brown635227e2006-07-01 16:48:23 -04001554 return -ENODEV;
Rich Townsend3f86b832006-07-01 11:36:54 -04001555 }
1556
Len Brown635227e2006-07-01 16:48:23 -04001557 return 0;
Rich Townsend3f86b832006-07-01 11:36:54 -04001558}
1559
1560static void __exit acpi_sbs_exit(void)
1561{
Rich Townsend3f86b832006-07-01 11:36:54 -04001562 acpi_bus_unregister_driver(&acpi_sbs_driver);
1563
Vladimir Lebedev72206232007-03-19 17:45:50 +03001564 acpi_sbs_rmdirs();
Rich Townsend3f86b832006-07-01 11:36:54 -04001565
Len Brown635227e2006-07-01 16:48:23 -04001566 return;
Rich Townsend3f86b832006-07-01 11:36:54 -04001567}
1568
1569module_init(acpi_sbs_init);
1570module_exit(acpi_sbs_exit);