blob: 9bdfcf50a190ca48dd88651725a2527f2867ee0a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04002 * battery.c - ACPI Battery Driver (Revision: 2.0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04004 * Copyright (C) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
5 * Copyright (C) 2004-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
7 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
8 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 *
25 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/types.h>
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040032#include <linux/jiffies.h>
Arjan van de Ven0f66af52009-01-10 14:19:05 -050033#include <linux/async.h>
Hector Martinbc76f902009-08-06 15:57:48 -070034#include <linux/dmi.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
Kyle McMartin25be5822011-03-22 16:19:50 -040036#include <linux/suspend.h>
Kamil Iskrab49dc132012-11-16 22:28:58 +010037#include <asm/unaligned.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040038
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +030039#ifdef CONFIG_ACPI_PROCFS_POWER
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <linux/proc_fs.h>
41#include <linux/seq_file.h>
42#include <asm/uaccess.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040043#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45#include <acpi/acpi_bus.h>
46#include <acpi/acpi_drivers.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040047#include <linux/power_supply.h>
48
Len Browna192a952009-07-28 16:45:54 -040049#define PREFIX "ACPI: "
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
52
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#define ACPI_BATTERY_CLASS "battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define ACPI_BATTERY_DEVICE_NAME "Battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define ACPI_BATTERY_NOTIFY_STATUS 0x80
56#define ACPI_BATTERY_NOTIFY_INFO 0x81
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040057#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Lan Tianyuae6f6182011-06-30 11:32:40 +080059/* Battery power unit: 0 means mW, 1 means mA */
60#define ACPI_BATTERY_POWER_UNIT_MA 1
61
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030063
Len Brownf52fd662007-02-12 22:42:12 -050064ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Len Brownf52fd662007-02-12 22:42:12 -050066MODULE_AUTHOR("Paul Diefenbaugh");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +040067MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
Len Brown7cda93e2007-02-12 23:50:02 -050068MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070069MODULE_LICENSE("GPL");
70
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040071static unsigned int cache_time = 1000;
72module_param(cache_time, uint, 0644);
73MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030074
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +030075#ifdef CONFIG_ACPI_PROCFS_POWER
Rich Townsend3f86b832006-07-01 11:36:54 -040076extern struct proc_dir_entry *acpi_lock_battery_dir(void);
77extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
78
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040079enum acpi_battery_files {
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +040080 info_tag = 0,
81 state_tag,
82 alarm_tag,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -040083 ACPI_BATTERY_NUMFILES,
84};
85
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040086#endif
87
88static const struct acpi_device_id battery_device_ids[] = {
89 {"PNP0C0A", 0},
90 {"", 0},
91};
92
93MODULE_DEVICE_TABLE(acpi, battery_device_ids);
94
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040095enum {
96 ACPI_BATTERY_ALARM_PRESENT,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040097 ACPI_BATTERY_XINFO_PRESENT,
Zhang Rui557d5862010-10-22 10:02:06 +080098 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
Kamil Iskrab49dc132012-11-16 22:28:58 +010099 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
100 switches between mWh and mAh depending on whether the system
101 is running on battery or not. When mAh is the unit, most
102 reported values are incorrect and need to be adjusted by
103 10000/design_voltage. Verified on x201, t410, t410s, and x220.
104 Pre-2010 and 2012 models appear to always report in mWh and
105 are thus unaffected (tested with t42, t61, t500, x200, x300,
106 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
107 the 2011 models that fixes the issue (tested on x220 with a
108 post-1.29 BIOS), but as of Nov. 2012, no such update is
109 available for the 2010 models. */
110 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400111};
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400112
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400113struct acpi_battery {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400114 struct mutex lock;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300115 struct mutex sysfs_lock;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400116 struct power_supply bat;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400117 struct acpi_device *device;
Kyle McMartin25be5822011-03-22 16:19:50 -0400118 struct notifier_block pm_nb;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400119 unsigned long update_time;
Lan Tianyuee9ef032013-07-30 14:00:42 +0200120 int revision;
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400121 int rate_now;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400122 int capacity_now;
123 int voltage_now;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400124 int design_capacity;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400125 int full_charge_capacity;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400126 int technology;
127 int design_voltage;
128 int design_capacity_warning;
129 int design_capacity_low;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400130 int cycle_count;
131 int measurement_accuracy;
132 int max_sampling_time;
133 int min_sampling_time;
134 int max_averaging_interval;
135 int min_averaging_interval;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400136 int capacity_granularity_1;
137 int capacity_granularity_2;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400138 int alarm;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400139 char model_number[32];
140 char serial_number[32];
141 char type[32];
142 char oem_info[32];
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400143 int state;
144 int power_unit;
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400145 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146};
147
Phil Carmody497888c2011-07-14 15:07:13 +0300148#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400149
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400150inline int acpi_battery_present(struct acpi_battery *battery)
151{
152 return battery->device->status.battery_present;
153}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400154
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400155static int acpi_battery_technology(struct acpi_battery *battery)
156{
157 if (!strcasecmp("NiCd", battery->type))
158 return POWER_SUPPLY_TECHNOLOGY_NiCd;
159 if (!strcasecmp("NiMH", battery->type))
160 return POWER_SUPPLY_TECHNOLOGY_NiMH;
161 if (!strcasecmp("LION", battery->type))
162 return POWER_SUPPLY_TECHNOLOGY_LION;
Andrey Borzenkovad40e682007-11-10 20:02:49 +0300163 if (!strncasecmp("LI-ION", battery->type, 6))
Alexey Starikovskiy0bde7ee2007-10-28 15:33:10 +0300164 return POWER_SUPPLY_TECHNOLOGY_LION;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400165 if (!strcasecmp("LiP", battery->type))
166 return POWER_SUPPLY_TECHNOLOGY_LIPO;
167 return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
168}
169
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300170static int acpi_battery_get_state(struct acpi_battery *battery);
Alexey Starikovskiyb19073a2007-10-25 17:10:47 -0400171
Richard Hughes56f382a2009-01-25 15:05:50 +0000172static int acpi_battery_is_charged(struct acpi_battery *battery)
173{
174 /* either charging or discharging */
175 if (battery->state != 0)
176 return 0;
177
178 /* battery not reporting charge */
179 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
180 battery->capacity_now == 0)
181 return 0;
182
183 /* good batteries update full_charge as the batteries degrade */
184 if (battery->full_charge_capacity == battery->capacity_now)
185 return 1;
186
187 /* fallback to using design values for broken batteries */
188 if (battery->design_capacity == battery->capacity_now)
189 return 1;
190
191 /* we don't do any sort of metric based on percentages */
192 return 0;
193}
194
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400195static int acpi_battery_get_property(struct power_supply *psy,
196 enum power_supply_property psp,
197 union power_supply_propval *val)
198{
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200199 int ret = 0;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400200 struct acpi_battery *battery = to_acpi_battery(psy);
201
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300202 if (acpi_battery_present(battery)) {
203 /* run battery update only if it is present */
204 acpi_battery_get_state(battery);
205 } else if (psp != POWER_SUPPLY_PROP_PRESENT)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400206 return -ENODEV;
207 switch (psp) {
208 case POWER_SUPPLY_PROP_STATUS:
209 if (battery->state & 0x01)
210 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
211 else if (battery->state & 0x02)
212 val->intval = POWER_SUPPLY_STATUS_CHARGING;
Richard Hughes56f382a2009-01-25 15:05:50 +0000213 else if (acpi_battery_is_charged(battery))
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400214 val->intval = POWER_SUPPLY_STATUS_FULL;
Roland Dreier4c41d3a2007-11-07 15:09:09 -0800215 else
216 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400217 break;
218 case POWER_SUPPLY_PROP_PRESENT:
219 val->intval = acpi_battery_present(battery);
220 break;
221 case POWER_SUPPLY_PROP_TECHNOLOGY:
222 val->intval = acpi_battery_technology(battery);
223 break;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400224 case POWER_SUPPLY_PROP_CYCLE_COUNT:
225 val->intval = battery->cycle_count;
226 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400227 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200228 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
229 ret = -ENODEV;
230 else
231 val->intval = battery->design_voltage * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400232 break;
233 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200234 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
235 ret = -ENODEV;
236 else
237 val->intval = battery->voltage_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400238 break;
239 case POWER_SUPPLY_PROP_CURRENT_NOW:
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400240 case POWER_SUPPLY_PROP_POWER_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200241 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
242 ret = -ENODEV;
243 else
244 val->intval = battery->rate_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400245 break;
246 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
247 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200248 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
249 ret = -ENODEV;
250 else
251 val->intval = battery->design_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400252 break;
253 case POWER_SUPPLY_PROP_CHARGE_FULL:
254 case POWER_SUPPLY_PROP_ENERGY_FULL:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200255 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
256 ret = -ENODEV;
257 else
258 val->intval = battery->full_charge_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400259 break;
260 case POWER_SUPPLY_PROP_CHARGE_NOW:
261 case POWER_SUPPLY_PROP_ENERGY_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200262 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
263 ret = -ENODEV;
264 else
265 val->intval = battery->capacity_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400266 break;
267 case POWER_SUPPLY_PROP_MODEL_NAME:
268 val->strval = battery->model_number;
269 break;
270 case POWER_SUPPLY_PROP_MANUFACTURER:
271 val->strval = battery->oem_info;
272 break;
maximilian attems7c2670b2008-01-22 18:46:50 +0100273 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
274 val->strval = battery->serial_number;
275 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400276 default:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200277 ret = -EINVAL;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400278 }
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200279 return ret;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400280}
281
282static enum power_supply_property charge_battery_props[] = {
283 POWER_SUPPLY_PROP_STATUS,
284 POWER_SUPPLY_PROP_PRESENT,
285 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400286 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400287 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
288 POWER_SUPPLY_PROP_VOLTAGE_NOW,
289 POWER_SUPPLY_PROP_CURRENT_NOW,
290 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
291 POWER_SUPPLY_PROP_CHARGE_FULL,
292 POWER_SUPPLY_PROP_CHARGE_NOW,
293 POWER_SUPPLY_PROP_MODEL_NAME,
294 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100295 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400296};
297
298static enum power_supply_property energy_battery_props[] = {
299 POWER_SUPPLY_PROP_STATUS,
300 POWER_SUPPLY_PROP_PRESENT,
301 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400302 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400303 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
304 POWER_SUPPLY_PROP_VOLTAGE_NOW,
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400305 POWER_SUPPLY_PROP_POWER_NOW,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400306 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
307 POWER_SUPPLY_PROP_ENERGY_FULL,
308 POWER_SUPPLY_PROP_ENERGY_NOW,
309 POWER_SUPPLY_PROP_MODEL_NAME,
310 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100311 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400312};
313
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +0300314#ifdef CONFIG_ACPI_PROCFS_POWER
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400315inline char *acpi_battery_units(struct acpi_battery *battery)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400316{
Lan Tianyuae6f6182011-06-30 11:32:40 +0800317 return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
318 "mA" : "mW";
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400319}
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400320#endif
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322/* --------------------------------------------------------------------------
323 Battery Management
324 -------------------------------------------------------------------------- */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400325struct acpi_offsets {
326 size_t offset; /* offset inside struct acpi_sbs_battery */
327 u8 mode; /* int or string? */
328};
329
330static struct acpi_offsets state_offsets[] = {
331 {offsetof(struct acpi_battery, state), 0},
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400332 {offsetof(struct acpi_battery, rate_now), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400333 {offsetof(struct acpi_battery, capacity_now), 0},
334 {offsetof(struct acpi_battery, voltage_now), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400335};
336
337static struct acpi_offsets info_offsets[] = {
338 {offsetof(struct acpi_battery, power_unit), 0},
339 {offsetof(struct acpi_battery, design_capacity), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400340 {offsetof(struct acpi_battery, full_charge_capacity), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400341 {offsetof(struct acpi_battery, technology), 0},
342 {offsetof(struct acpi_battery, design_voltage), 0},
343 {offsetof(struct acpi_battery, design_capacity_warning), 0},
344 {offsetof(struct acpi_battery, design_capacity_low), 0},
345 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
346 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
347 {offsetof(struct acpi_battery, model_number), 1},
348 {offsetof(struct acpi_battery, serial_number), 1},
349 {offsetof(struct acpi_battery, type), 1},
350 {offsetof(struct acpi_battery, oem_info), 1},
351};
352
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400353static struct acpi_offsets extended_info_offsets[] = {
Lan Tianyuee9ef032013-07-30 14:00:42 +0200354 {offsetof(struct acpi_battery, revision), 0},
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400355 {offsetof(struct acpi_battery, power_unit), 0},
356 {offsetof(struct acpi_battery, design_capacity), 0},
357 {offsetof(struct acpi_battery, full_charge_capacity), 0},
358 {offsetof(struct acpi_battery, technology), 0},
359 {offsetof(struct acpi_battery, design_voltage), 0},
360 {offsetof(struct acpi_battery, design_capacity_warning), 0},
361 {offsetof(struct acpi_battery, design_capacity_low), 0},
362 {offsetof(struct acpi_battery, cycle_count), 0},
363 {offsetof(struct acpi_battery, measurement_accuracy), 0},
364 {offsetof(struct acpi_battery, max_sampling_time), 0},
365 {offsetof(struct acpi_battery, min_sampling_time), 0},
366 {offsetof(struct acpi_battery, max_averaging_interval), 0},
367 {offsetof(struct acpi_battery, min_averaging_interval), 0},
368 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
369 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
370 {offsetof(struct acpi_battery, model_number), 1},
371 {offsetof(struct acpi_battery, serial_number), 1},
372 {offsetof(struct acpi_battery, type), 1},
373 {offsetof(struct acpi_battery, oem_info), 1},
374};
375
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400376static int extract_package(struct acpi_battery *battery,
377 union acpi_object *package,
378 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300379{
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300380 int i;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400381 union acpi_object *element;
382 if (package->type != ACPI_TYPE_PACKAGE)
383 return -EFAULT;
384 for (i = 0; i < num; ++i) {
385 if (package->package.count <= i)
386 return -EFAULT;
387 element = &package->package.elements[i];
388 if (offsets[i].mode) {
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300389 u8 *ptr = (u8 *)battery + offsets[i].offset;
390 if (element->type == ACPI_TYPE_STRING ||
391 element->type == ACPI_TYPE_BUFFER)
392 strncpy(ptr, element->string.pointer, 32);
393 else if (element->type == ACPI_TYPE_INTEGER) {
394 strncpy(ptr, (u8 *)&element->integer.value,
Lin Ming439913f2010-01-28 10:53:19 +0800395 sizeof(u64));
396 ptr[sizeof(u64)] = 0;
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400397 } else
398 *ptr = 0; /* don't have value */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400399 } else {
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400400 int *x = (int *)((u8 *)battery + offsets[i].offset);
401 *x = (element->type == ACPI_TYPE_INTEGER) ?
402 element->integer.value : -1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300403 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300404 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300405 return 0;
406}
407
408static int acpi_battery_get_status(struct acpi_battery *battery)
409{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400410 if (acpi_bus_get_status(battery->device)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300411 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
412 return -ENODEV;
413 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400414 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300415}
416
417static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400419 int result = -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400420 acpi_status status = 0;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400421 char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags)?
422 "_BIX" : "_BIF";
423
Len Brown4be44fc2005-08-05 00:44:28 -0400424 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300426 if (!acpi_battery_present(battery))
427 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400428 mutex_lock(&battery->lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400429 status = acpi_evaluate_object(battery->device->handle, name,
430 NULL, &buffer);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400431 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400432
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 if (ACPI_FAILURE(status)) {
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400434 ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
Patrick Mocheld550d982006-06-27 00:41:40 -0400435 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400437 if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
438 result = extract_package(battery, buffer.pointer,
439 extended_info_offsets,
440 ARRAY_SIZE(extended_info_offsets));
441 else
442 result = extract_package(battery, buffer.pointer,
443 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400444 kfree(buffer.pointer);
Zhang Rui557d5862010-10-22 10:02:06 +0800445 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
446 battery->full_charge_capacity = battery->design_capacity;
Kamil Iskrab49dc132012-11-16 22:28:58 +0100447 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
448 battery->power_unit && battery->design_voltage) {
449 battery->design_capacity = battery->design_capacity *
450 10000 / battery->design_voltage;
451 battery->full_charge_capacity = battery->full_charge_capacity *
452 10000 / battery->design_voltage;
453 battery->design_capacity_warning =
454 battery->design_capacity_warning *
455 10000 / battery->design_voltage;
456 /* Curiously, design_capacity_low, unlike the rest of them,
457 is correct. */
458 /* capacity_granularity_* equal 1 on the systems tested, so
459 it's impossible to tell if they would need an adjustment
460 or not if their values were higher. */
461 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400462 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300465static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
Len Brown4be44fc2005-08-05 00:44:28 -0400467 int result = 0;
468 acpi_status status = 0;
469 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300471 if (!acpi_battery_present(battery))
472 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400474 if (battery->update_time &&
475 time_before(jiffies, battery->update_time +
476 msecs_to_jiffies(cache_time)))
477 return 0;
478
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400479 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400480 status = acpi_evaluate_object(battery->device->handle, "_BST",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400481 NULL, &buffer);
482 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400485 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400486 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400488
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400489 result = extract_package(battery, buffer.pointer,
490 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400491 battery->update_time = jiffies;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400492 kfree(buffer.pointer);
Hector Martinbc76f902009-08-06 15:57:48 -0700493
Lan Tianyu55003b22011-06-30 11:33:12 +0800494 /* For buggy DSDTs that report negative 16-bit values for either
495 * charging or discharging current and/or report 0 as 65536
496 * due to bad math.
497 */
498 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
499 battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
500 (s16)(battery->rate_now) < 0) {
Hector Martinbc76f902009-08-06 15:57:48 -0700501 battery->rate_now = abs((s16)battery->rate_now);
Lan Tianyu55003b22011-06-30 11:33:12 +0800502 printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
503 " invalid.\n");
504 }
Hector Martinbc76f902009-08-06 15:57:48 -0700505
Zhang Rui557d5862010-10-22 10:02:06 +0800506 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
507 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
508 battery->capacity_now = (battery->capacity_now *
509 battery->full_charge_capacity) / 100;
Kamil Iskrab49dc132012-11-16 22:28:58 +0100510 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
511 battery->power_unit && battery->design_voltage) {
512 battery->capacity_now = battery->capacity_now *
513 10000 / battery->design_voltage;
514 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400515 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400518static int acpi_battery_set_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519{
Len Brown4be44fc2005-08-05 00:44:28 -0400520 acpi_status status = 0;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400521 union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER };
Len Brown4be44fc2005-08-05 00:44:28 -0400522 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400524 if (!acpi_battery_present(battery) ||
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400525 !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300526 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400528 arg0.integer.value = battery->alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400530 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400531 status = acpi_evaluate_object(battery->device->handle, "_BTP",
532 &arg_list, NULL);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400533 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400534
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400536 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400538 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm));
Patrick Mocheld550d982006-06-27 00:41:40 -0400539 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300542static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Len Brown4be44fc2005-08-05 00:44:28 -0400544 acpi_status status = AE_OK;
545 acpi_handle handle = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300547 /* See if alarms are supported, and if so, set default */
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400548 status = acpi_get_handle(battery->device->handle, "_BTP", &handle);
549 if (ACPI_FAILURE(status)) {
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400550 clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400551 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 }
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400553 set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400554 if (!battery->alarm)
555 battery->alarm = battery->design_capacity_warning;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400556 return acpi_battery_set_alarm(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557}
558
Andrey Borzenkov508df922007-10-28 12:50:09 +0300559static ssize_t acpi_battery_alarm_show(struct device *dev,
560 struct device_attribute *attr,
561 char *buf)
562{
563 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
564 return sprintf(buf, "%d\n", battery->alarm * 1000);
565}
566
567static ssize_t acpi_battery_alarm_store(struct device *dev,
568 struct device_attribute *attr,
569 const char *buf, size_t count)
570{
571 unsigned long x;
572 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
573 if (sscanf(buf, "%ld\n", &x) == 1)
574 battery->alarm = x/1000;
575 if (acpi_battery_present(battery))
576 acpi_battery_set_alarm(battery);
577 return count;
578}
579
580static struct device_attribute alarm_attr = {
Parag Warudkar01e8ef12008-10-18 20:28:50 -0700581 .attr = {.name = "alarm", .mode = 0644},
Andrey Borzenkov508df922007-10-28 12:50:09 +0300582 .show = acpi_battery_alarm_show,
583 .store = acpi_battery_alarm_store,
584};
585
586static int sysfs_add_battery(struct acpi_battery *battery)
587{
588 int result;
589
Lan Tianyuae6f6182011-06-30 11:32:40 +0800590 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
Andrey Borzenkov508df922007-10-28 12:50:09 +0300591 battery->bat.properties = charge_battery_props;
592 battery->bat.num_properties =
593 ARRAY_SIZE(charge_battery_props);
594 } else {
595 battery->bat.properties = energy_battery_props;
596 battery->bat.num_properties =
597 ARRAY_SIZE(energy_battery_props);
598 }
599
600 battery->bat.name = acpi_device_bid(battery->device);
601 battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
602 battery->bat.get_property = acpi_battery_get_property;
603
604 result = power_supply_register(&battery->device->dev, &battery->bat);
605 if (result)
606 return result;
607 return device_create_file(battery->bat.dev, &alarm_attr);
608}
609
610static void sysfs_remove_battery(struct acpi_battery *battery)
611{
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300612 mutex_lock(&battery->sysfs_lock);
Lan Tianyu9c921c22011-06-30 11:34:12 +0800613 if (!battery->bat.dev) {
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300614 mutex_unlock(&battery->sysfs_lock);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300615 return;
Lan Tianyu9c921c22011-06-30 11:34:12 +0800616 }
617
Andrey Borzenkov508df922007-10-28 12:50:09 +0300618 device_remove_file(battery->bat.dev, &alarm_attr);
619 power_supply_unregister(&battery->bat);
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300620 battery->bat.dev = NULL;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300621 mutex_unlock(&battery->sysfs_lock);
Hector Martinbc76f902009-08-06 15:57:48 -0700622}
623
Kamil Iskrab49dc132012-11-16 22:28:58 +0100624static void find_battery(const struct dmi_header *dm, void *private)
625{
626 struct acpi_battery *battery = (struct acpi_battery *)private;
627 /* Note: the hardcoded offsets below have been extracted from
628 the source code of dmidecode. */
629 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
630 const u8 *dmi_data = (const u8 *)(dm + 1);
631 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
632 if (dm->length >= 18)
633 dmi_capacity *= dmi_data[17];
634 if (battery->design_capacity * battery->design_voltage / 1000
635 != dmi_capacity &&
636 battery->design_capacity * 10 == dmi_capacity)
637 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
638 &battery->flags);
639 }
640}
641
Zhang Rui557d5862010-10-22 10:02:06 +0800642/*
643 * According to the ACPI spec, some kinds of primary batteries can
644 * report percentage battery remaining capacity directly to OS.
645 * In this case, it reports the Last Full Charged Capacity == 100
646 * and BatteryPresentRate == 0xFFFFFFFF.
647 *
648 * Now we found some battery reports percentage remaining capacity
649 * even if it's rechargeable.
650 * https://bugzilla.kernel.org/show_bug.cgi?id=15979
651 *
652 * Handle this correctly so that they won't break userspace.
653 */
Lan Tianyu7b786222011-06-30 11:33:27 +0800654static void acpi_battery_quirks(struct acpi_battery *battery)
Zhang Rui557d5862010-10-22 10:02:06 +0800655{
656 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
657 return ;
658
659 if (battery->full_charge_capacity == 100 &&
660 battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
661 battery->capacity_now >=0 && battery->capacity_now <= 100) {
662 set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
663 battery->full_charge_capacity = battery->design_capacity;
664 battery->capacity_now = (battery->capacity_now *
665 battery->full_charge_capacity) / 100;
666 }
Kamil Iskrab49dc132012-11-16 22:28:58 +0100667
668 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
669 return ;
670
671 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
672 const char *s;
673 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
674 if (s && !strnicmp(s, "ThinkPad", 8)) {
675 dmi_walk(find_battery, battery);
676 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
677 &battery->flags) &&
678 battery->design_voltage) {
679 battery->design_capacity =
680 battery->design_capacity *
681 10000 / battery->design_voltage;
682 battery->full_charge_capacity =
683 battery->full_charge_capacity *
684 10000 / battery->design_voltage;
685 battery->design_capacity_warning =
686 battery->design_capacity_warning *
687 10000 / battery->design_voltage;
688 battery->capacity_now = battery->capacity_now *
689 10000 / battery->design_voltage;
690 }
691 }
692 }
Zhang Rui557d5862010-10-22 10:02:06 +0800693}
694
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400695static int acpi_battery_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500696{
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300697 int result, old_present = acpi_battery_present(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500698 result = acpi_battery_get_status(battery);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300699 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300700 return result;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300701 if (!acpi_battery_present(battery)) {
702 sysfs_remove_battery(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500703 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300704 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300705 }
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300706 if (!battery->update_time ||
707 old_present != acpi_battery_present(battery)) {
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500708 result = acpi_battery_get_info(battery);
709 if (result)
710 return result;
711 acpi_battery_init_alarm(battery);
712 }
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100713 if (!battery->bat.dev) {
714 result = sysfs_add_battery(battery);
715 if (result)
716 return result;
717 }
Zhang Rui557d5862010-10-22 10:02:06 +0800718 result = acpi_battery_get_state(battery);
Lan Tianyu7b786222011-06-30 11:33:27 +0800719 acpi_battery_quirks(battery);
Zhang Rui557d5862010-10-22 10:02:06 +0800720 return result;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500721}
722
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100723static void acpi_battery_refresh(struct acpi_battery *battery)
724{
Andy Whitcroftcd08f772012-05-03 14:48:26 +0100725 int power_unit;
726
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100727 if (!battery->bat.dev)
728 return;
729
Andy Whitcroftcd08f772012-05-03 14:48:26 +0100730 power_unit = battery->power_unit;
731
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100732 acpi_battery_get_info(battery);
Andy Whitcroftcd08f772012-05-03 14:48:26 +0100733
734 if (power_unit == battery->power_unit)
735 return;
736
737 /* The battery has changed its reporting units. */
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100738 sysfs_remove_battery(battery);
739 sysfs_add_battery(battery);
740}
741
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742/* --------------------------------------------------------------------------
743 FS Interface (/proc)
744 -------------------------------------------------------------------------- */
745
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +0300746#ifdef CONFIG_ACPI_PROCFS_POWER
Len Brown4be44fc2005-08-05 00:44:28 -0400747static struct proc_dir_entry *acpi_battery_dir;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300748
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400749static int acpi_battery_print_info(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200751 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300753 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 goto end;
755
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400756 seq_printf(seq, "present: %s\n",
757 acpi_battery_present(battery)?"yes":"no");
758 if (!acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 goto end;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400760 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 seq_printf(seq, "design capacity: unknown\n");
762 else
763 seq_printf(seq, "design capacity: %d %sh\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400764 battery->design_capacity,
765 acpi_battery_units(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400767 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 seq_printf(seq, "last full capacity: unknown\n");
769 else
770 seq_printf(seq, "last full capacity: %d %sh\n",
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400771 battery->full_charge_capacity,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400772 acpi_battery_units(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400774 seq_printf(seq, "battery technology: %srechargeable\n",
775 (!battery->technology)?"non-":"");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400777 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 seq_printf(seq, "design voltage: unknown\n");
779 else
780 seq_printf(seq, "design voltage: %d mV\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400781 battery->design_voltage);
Len Brown4be44fc2005-08-05 00:44:28 -0400782 seq_printf(seq, "design capacity warning: %d %sh\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400783 battery->design_capacity_warning,
784 acpi_battery_units(battery));
Len Brown4be44fc2005-08-05 00:44:28 -0400785 seq_printf(seq, "design capacity low: %d %sh\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400786 battery->design_capacity_low,
787 acpi_battery_units(battery));
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400788 seq_printf(seq, "cycle count: %i\n", battery->cycle_count);
Len Brown4be44fc2005-08-05 00:44:28 -0400789 seq_printf(seq, "capacity granularity 1: %d %sh\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400790 battery->capacity_granularity_1,
791 acpi_battery_units(battery));
Len Brown4be44fc2005-08-05 00:44:28 -0400792 seq_printf(seq, "capacity granularity 2: %d %sh\n",
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400793 battery->capacity_granularity_2,
794 acpi_battery_units(battery));
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400795 seq_printf(seq, "model number: %s\n", battery->model_number);
796 seq_printf(seq, "serial number: %s\n", battery->serial_number);
797 seq_printf(seq, "battery type: %s\n", battery->type);
798 seq_printf(seq, "OEM info: %s\n", battery->oem_info);
Len Brown4be44fc2005-08-05 00:44:28 -0400799 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300800 if (result)
801 seq_printf(seq, "ERROR: Unable to read battery info\n");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300802 return result;
803}
804
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400805static int acpi_battery_print_state(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200807 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300809 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 goto end;
811
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400812 seq_printf(seq, "present: %s\n",
813 acpi_battery_present(battery)?"yes":"no");
814 if (!acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400817 seq_printf(seq, "capacity state: %s\n",
818 (battery->state & 0x04)?"critical":"ok");
819 if ((battery->state & 0x01) && (battery->state & 0x02))
Len Brown4be44fc2005-08-05 00:44:28 -0400820 seq_printf(seq,
821 "charging state: charging/discharging\n");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400822 else if (battery->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 seq_printf(seq, "charging state: discharging\n");
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400824 else if (battery->state & 0x02)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 seq_printf(seq, "charging state: charging\n");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400826 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 seq_printf(seq, "charging state: charged\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400829 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 seq_printf(seq, "present rate: unknown\n");
831 else
832 seq_printf(seq, "present rate: %d %s\n",
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400833 battery->rate_now, acpi_battery_units(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400835 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 seq_printf(seq, "remaining capacity: unknown\n");
837 else
838 seq_printf(seq, "remaining capacity: %d %sh\n",
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400839 battery->capacity_now, acpi_battery_units(battery));
840 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 seq_printf(seq, "present voltage: unknown\n");
842 else
843 seq_printf(seq, "present voltage: %d mV\n",
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400844 battery->voltage_now);
Len Brown4be44fc2005-08-05 00:44:28 -0400845 end:
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400846 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300847 seq_printf(seq, "ERROR: Unable to read battery state\n");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300848
849 return result;
850}
851
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400852static int acpi_battery_print_alarm(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200854 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300856 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 goto end;
858
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300859 if (!acpi_battery_present(battery)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 seq_printf(seq, "present: no\n");
861 goto end;
862 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 seq_printf(seq, "alarm: ");
864 if (!battery->alarm)
865 seq_printf(seq, "unsupported\n");
866 else
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400867 seq_printf(seq, "%u %sh\n", battery->alarm,
868 acpi_battery_units(battery));
Len Brown4be44fc2005-08-05 00:44:28 -0400869 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300870 if (result)
871 seq_printf(seq, "ERROR: Unable to read battery alarm\n");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300872 return result;
873}
874
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400875static ssize_t acpi_battery_write_alarm(struct file *file,
876 const char __user * buffer,
877 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
Len Brown4be44fc2005-08-05 00:44:28 -0400879 int result = 0;
880 char alarm_string[12] = { '\0' };
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200881 struct seq_file *m = file->private_data;
882 struct acpi_battery *battery = m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400885 return -EINVAL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300886 if (!acpi_battery_present(battery)) {
887 result = -ENODEV;
888 goto end;
889 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300890 if (copy_from_user(alarm_string, buffer, count)) {
891 result = -EFAULT;
892 goto end;
893 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 alarm_string[count] = '\0';
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400895 battery->alarm = simple_strtol(alarm_string, NULL, 0);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400896 result = acpi_battery_set_alarm(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300897 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300898 if (!result)
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400899 return count;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300900 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901}
902
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400903typedef int(*print_func)(struct seq_file *seq, int result);
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400904
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400905static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = {
906 acpi_battery_print_info,
907 acpi_battery_print_state,
908 acpi_battery_print_alarm,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400909};
910
911static int acpi_battery_read(int fid, struct seq_file *seq)
912{
913 struct acpi_battery *battery = seq->private;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400914 int result = acpi_battery_update(battery);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400915 return acpi_print_funcs[fid](seq, result);
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400916}
917
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400918#define DECLARE_FILE_FUNCTIONS(_name) \
919static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \
920{ \
921 return acpi_battery_read(_name##_tag, seq); \
922} \
923static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \
924{ \
925 return single_open(file, acpi_battery_read_##_name, PDE(inode)->data); \
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400926}
927
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400928DECLARE_FILE_FUNCTIONS(info);
929DECLARE_FILE_FUNCTIONS(state);
930DECLARE_FILE_FUNCTIONS(alarm);
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400931
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400932#undef DECLARE_FILE_FUNCTIONS
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400933
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400934#define FILE_DESCRIPTION_RO(_name) \
935 { \
936 .name = __stringify(_name), \
937 .mode = S_IRUGO, \
938 .ops = { \
939 .open = acpi_battery_##_name##_open_fs, \
940 .read = seq_read, \
941 .llseek = seq_lseek, \
942 .release = single_release, \
943 .owner = THIS_MODULE, \
944 }, \
945 }
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400946
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400947#define FILE_DESCRIPTION_RW(_name) \
948 { \
949 .name = __stringify(_name), \
950 .mode = S_IFREG | S_IRUGO | S_IWUSR, \
951 .ops = { \
952 .open = acpi_battery_##_name##_open_fs, \
953 .read = seq_read, \
954 .llseek = seq_lseek, \
955 .write = acpi_battery_write_##_name, \
956 .release = single_release, \
957 .owner = THIS_MODULE, \
958 }, \
959 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
Vasiliy Kulikov9c8b04b2011-06-25 21:07:52 +0400961static const struct battery_file {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400962 struct file_operations ops;
Al Virod161a132011-07-24 03:36:29 -0400963 umode_t mode;
Jan Engelhardt070d8eb2009-01-12 00:07:55 +0100964 const char *name;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400965} acpi_battery_file[] = {
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400966 FILE_DESCRIPTION_RO(info),
967 FILE_DESCRIPTION_RO(state),
968 FILE_DESCRIPTION_RW(alarm),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969};
970
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400971#undef FILE_DESCRIPTION_RO
972#undef FILE_DESCRIPTION_RW
973
Len Brown4be44fc2005-08-05 00:44:28 -0400974static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
Len Brown4be44fc2005-08-05 00:44:28 -0400976 struct proc_dir_entry *entry = NULL;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400977 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
Zhang Rui6d855fc2011-01-10 11:16:30 +0800979 printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded,"
980 " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 if (!acpi_device_dir(device)) {
982 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400983 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400985 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 }
987
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400988 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
Denis V. Lunevcf7acfa2008-04-29 01:02:27 -0700989 entry = proc_create_data(acpi_battery_file[i].name,
990 acpi_battery_file[i].mode,
991 acpi_device_dir(device),
992 &acpi_battery_file[i].ops,
993 acpi_driver_data(device));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400994 if (!entry)
995 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400997 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998}
999
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001000static void acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001{
Alexey Starikovskiy78490d82007-05-11 13:18:55 -04001002 int i;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001003 if (!acpi_device_dir(device))
1004 return;
1005 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i)
1006 remove_proc_entry(acpi_battery_file[i].name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001009 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
1010 acpi_device_dir(device) = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011}
1012
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001013#endif
Alexey Starikovskiy3e58ea02007-09-26 19:43:11 +04001014
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015/* --------------------------------------------------------------------------
1016 Driver Interface
1017 -------------------------------------------------------------------------- */
1018
Bjorn Helgaasd9406692009-04-30 09:35:47 -06001019static void acpi_battery_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020{
Bjorn Helgaasd9406692009-04-30 09:35:47 -06001021 struct acpi_battery *battery = acpi_driver_data(device);
Zhang Rui153e5002010-07-07 09:11:57 +08001022 struct device *old;
Bjorn Helgaasd9406692009-04-30 09:35:47 -06001023
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -04001025 return;
Zhang Rui153e5002010-07-07 09:11:57 +08001026 old = battery->bat.dev;
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +01001027 if (event == ACPI_BATTERY_NOTIFY_INFO)
1028 acpi_battery_refresh(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +04001029 acpi_battery_update(battery);
1030 acpi_bus_generate_proc_event(device, event,
1031 acpi_battery_present(battery));
1032 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +01001033 dev_name(&device->dev), event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +03001034 acpi_battery_present(battery));
Justin P. Mattock2345baf2009-12-13 14:42:36 -08001035 /* acpi_battery_update could remove power_supply object */
Zhang Rui153e5002010-07-07 09:11:57 +08001036 if (old && battery->bat.dev)
Alan Jenkinsf79e1ce2009-06-30 14:36:16 +00001037 power_supply_changed(&battery->bat);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038}
1039
Kyle McMartin25be5822011-03-22 16:19:50 -04001040static int battery_notify(struct notifier_block *nb,
1041 unsigned long mode, void *_unused)
1042{
1043 struct acpi_battery *battery = container_of(nb, struct acpi_battery,
1044 pm_nb);
1045 switch (mode) {
Lan Tianyud5a59112011-06-30 11:33:40 +08001046 case PM_POST_HIBERNATION:
Kyle McMartin25be5822011-03-22 16:19:50 -04001047 case PM_POST_SUSPEND:
Lan Tianyu6e17fb62011-06-30 11:33:58 +08001048 if (battery->bat.dev) {
1049 sysfs_remove_battery(battery);
1050 sysfs_add_battery(battery);
1051 }
Kyle McMartin25be5822011-03-22 16:19:50 -04001052 break;
1053 }
1054
1055 return 0;
1056}
1057
Len Brown4be44fc2005-08-05 00:44:28 -04001058static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
Len Brown4be44fc2005-08-05 00:44:28 -04001060 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -04001061 struct acpi_battery *battery = NULL;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +04001062 acpi_handle handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001064 return -EINVAL;
Burman Yan36bcbec2006-12-19 12:56:11 -08001065 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -04001067 return -ENOMEM;
Patrick Mochel145def82006-05-19 16:54:39 -04001068 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
1070 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -07001071 device->driver_data = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +04001072 mutex_init(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +03001073 mutex_init(&battery->sysfs_lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +04001074 if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
1075 "_BIX", &handle)))
1076 set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +01001077 result = acpi_battery_update(battery);
1078 if (result)
1079 goto fail;
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001080#ifdef CONFIG_ACPI_PROCFS_POWER
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 result = acpi_battery_add_fs(device);
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001082#endif
Stefan Hajnoczie80bba42011-07-12 09:03:28 +01001083 if (result) {
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001084#ifdef CONFIG_ACPI_PROCFS_POWER
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 acpi_battery_remove_fs(device);
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001086#endif
Stefan Hajnoczie80bba42011-07-12 09:03:28 +01001087 goto fail;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 }
Kyle McMartin25be5822011-03-22 16:19:50 -04001089
Stefan Hajnoczie80bba42011-07-12 09:03:28 +01001090 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
1091 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
1092 device->status.battery_present ? "present" : "absent");
1093
Kyle McMartin25be5822011-03-22 16:19:50 -04001094 battery->pm_nb.notifier_call = battery_notify;
1095 register_pm_notifier(&battery->pm_nb);
1096
Patrick Mocheld550d982006-06-27 00:41:40 -04001097 return result;
Stefan Hajnoczie80bba42011-07-12 09:03:28 +01001098
1099fail:
1100 sysfs_remove_battery(battery);
1101 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +03001102 mutex_destroy(&battery->sysfs_lock);
Stefan Hajnoczie80bba42011-07-12 09:03:28 +01001103 kfree(battery);
1104 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105}
1106
Len Brown4be44fc2005-08-05 00:44:28 -04001107static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108{
Len Brown4be44fc2005-08-05 00:44:28 -04001109 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001112 return -EINVAL;
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001113 battery = acpi_driver_data(device);
Kyle McMartin25be5822011-03-22 16:19:50 -04001114 unregister_pm_notifier(&battery->pm_nb);
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001115#ifdef CONFIG_ACPI_PROCFS_POWER
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 acpi_battery_remove_fs(device);
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001117#endif
Andrey Borzenkov508df922007-10-28 12:50:09 +03001118 sysfs_remove_battery(battery);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +04001119 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +03001120 mutex_destroy(&battery->sysfs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 kfree(battery);
Patrick Mocheld550d982006-06-27 00:41:40 -04001122 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123}
1124
Jiri Kosina34c44152006-10-10 14:20:41 -07001125/* this is needed to learn about changes made in suspended state */
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001126static int acpi_battery_resume(struct acpi_device *device)
Jiri Kosina34c44152006-10-10 14:20:41 -07001127{
1128 struct acpi_battery *battery;
Jiri Kosina34c44152006-10-10 14:20:41 -07001129 if (!device)
1130 return -EINVAL;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +04001131 battery = acpi_driver_data(device);
1132 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +03001133 acpi_battery_update(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001134 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -07001135}
1136
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001137static struct acpi_driver acpi_battery_driver = {
1138 .name = "battery",
1139 .class = ACPI_BATTERY_CLASS,
1140 .ids = battery_device_ids,
Bjorn Helgaasd9406692009-04-30 09:35:47 -06001141 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001142 .ops = {
1143 .add = acpi_battery_add,
1144 .resume = acpi_battery_resume,
1145 .remove = acpi_battery_remove,
Bjorn Helgaasd9406692009-04-30 09:35:47 -06001146 .notify = acpi_battery_notify,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001147 },
1148};
1149
Linus Torvaldsb0cbc862009-04-11 12:45:20 -07001150static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151{
Pavel Machek4d8316d2006-08-14 22:37:22 -07001152 if (acpi_disabled)
Arjan van de Ven0f66af52009-01-10 14:19:05 -05001153 return;
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001154#ifdef CONFIG_ACPI_PROCFS_POWER
Rich Townsend3f86b832006-07-01 11:36:54 -04001155 acpi_battery_dir = acpi_lock_battery_dir();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 if (!acpi_battery_dir)
Arjan van de Ven0f66af52009-01-10 14:19:05 -05001157 return;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001158#endif
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +04001159 if (acpi_bus_register_driver(&acpi_battery_driver) < 0) {
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001160#ifdef CONFIG_ACPI_PROCFS_POWER
Rich Townsend3f86b832006-07-01 11:36:54 -04001161 acpi_unlock_battery_dir(acpi_battery_dir);
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001162#endif
Arjan van de Ven0f66af52009-01-10 14:19:05 -05001163 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 }
Arjan van de Ven0f66af52009-01-10 14:19:05 -05001165 return;
1166}
1167
1168static int __init acpi_battery_init(void)
1169{
1170 async_schedule(acpi_battery_init_async, NULL);
Patrick Mocheld550d982006-06-27 00:41:40 -04001171 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172}
1173
Len Brown4be44fc2005-08-05 00:44:28 -04001174static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 acpi_bus_unregister_driver(&acpi_battery_driver);
Alexey Starikovskiyfdcedbb2007-11-19 16:33:45 +03001177#ifdef CONFIG_ACPI_PROCFS_POWER
Rich Townsend3f86b832006-07-01 11:36:54 -04001178 acpi_unlock_battery_dir(acpi_battery_dir);
Alexey Starikovskiyd7380962007-09-26 19:43:04 +04001179#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180}
1181
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182module_init(acpi_battery_init);
1183module_exit(acpi_battery_exit);