blob: fbf1aceda8b8ab915a7d2476d78db2b6e2b94e68 [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 Iskra4000e622012-11-16 22:28:58 +010037#include <asm/unaligned.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040038
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <acpi/acpi_bus.h>
40#include <acpi/acpi_drivers.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040041#include <linux/power_supply.h>
42
Len Browna192a952009-07-28 16:45:54 -040043#define PREFIX "ACPI: "
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
46
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define ACPI_BATTERY_CLASS "battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define ACPI_BATTERY_DEVICE_NAME "Battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#define ACPI_BATTERY_NOTIFY_STATUS 0x80
50#define ACPI_BATTERY_NOTIFY_INFO 0x81
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040051#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
Lan Tianyuae6f6182011-06-30 11:32:40 +080053/* Battery power unit: 0 means mW, 1 means mA */
54#define ACPI_BATTERY_POWER_UNIT_MA 1
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030057
Len Brownf52fd662007-02-12 22:42:12 -050058ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Len Brownf52fd662007-02-12 22:42:12 -050060MODULE_AUTHOR("Paul Diefenbaugh");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +040061MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
Len Brown7cda93e2007-02-12 23:50:02 -050062MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070063MODULE_LICENSE("GPL");
64
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040065static unsigned int cache_time = 1000;
66module_param(cache_time, uint, 0644);
67MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030068
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040069static const struct acpi_device_id battery_device_ids[] = {
70 {"PNP0C0A", 0},
71 {"", 0},
72};
73
74MODULE_DEVICE_TABLE(acpi, battery_device_ids);
75
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040076enum {
77 ACPI_BATTERY_ALARM_PRESENT,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040078 ACPI_BATTERY_XINFO_PRESENT,
Zhang Rui557d5862010-10-22 10:02:06 +080079 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
Kamil Iskra4000e622012-11-16 22:28:58 +010080 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
81 switches between mWh and mAh depending on whether the system
82 is running on battery or not. When mAh is the unit, most
83 reported values are incorrect and need to be adjusted by
84 10000/design_voltage. Verified on x201, t410, t410s, and x220.
85 Pre-2010 and 2012 models appear to always report in mWh and
86 are thus unaffected (tested with t42, t61, t500, x200, x300,
87 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
88 the 2011 models that fixes the issue (tested on x220 with a
89 post-1.29 BIOS), but as of Nov. 2012, no such update is
90 available for the 2010 models. */
91 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040092};
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040093
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040094struct acpi_battery {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040095 struct mutex lock;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +030096 struct mutex sysfs_lock;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040097 struct power_supply bat;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040098 struct acpi_device *device;
Kyle McMartin25be5822011-03-22 16:19:50 -040099 struct notifier_block pm_nb;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400100 unsigned long update_time;
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200101 int revision;
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400102 int rate_now;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400103 int capacity_now;
104 int voltage_now;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400105 int design_capacity;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400106 int full_charge_capacity;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400107 int technology;
108 int design_voltage;
109 int design_capacity_warning;
110 int design_capacity_low;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400111 int cycle_count;
112 int measurement_accuracy;
113 int max_sampling_time;
114 int min_sampling_time;
115 int max_averaging_interval;
116 int min_averaging_interval;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400117 int capacity_granularity_1;
118 int capacity_granularity_2;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400119 int alarm;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400120 char model_number[32];
121 char serial_number[32];
122 char type[32];
123 char oem_info[32];
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400124 int state;
125 int power_unit;
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400126 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127};
128
Phil Carmody497888c2011-07-14 15:07:13 +0300129#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400130
Andy Shevchenkoefd941f2013-03-11 09:17:06 +0000131static inline int acpi_battery_present(struct acpi_battery *battery)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400132{
133 return battery->device->status.battery_present;
134}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400135
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400136static int acpi_battery_technology(struct acpi_battery *battery)
137{
138 if (!strcasecmp("NiCd", battery->type))
139 return POWER_SUPPLY_TECHNOLOGY_NiCd;
140 if (!strcasecmp("NiMH", battery->type))
141 return POWER_SUPPLY_TECHNOLOGY_NiMH;
142 if (!strcasecmp("LION", battery->type))
143 return POWER_SUPPLY_TECHNOLOGY_LION;
Andrey Borzenkovad40e682007-11-10 20:02:49 +0300144 if (!strncasecmp("LI-ION", battery->type, 6))
Alexey Starikovskiy0bde7ee2007-10-28 15:33:10 +0300145 return POWER_SUPPLY_TECHNOLOGY_LION;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400146 if (!strcasecmp("LiP", battery->type))
147 return POWER_SUPPLY_TECHNOLOGY_LIPO;
148 return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
149}
150
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300151static int acpi_battery_get_state(struct acpi_battery *battery);
Alexey Starikovskiyb19073a2007-10-25 17:10:47 -0400152
Richard Hughes56f382a2009-01-25 15:05:50 +0000153static int acpi_battery_is_charged(struct acpi_battery *battery)
154{
155 /* either charging or discharging */
156 if (battery->state != 0)
157 return 0;
158
159 /* battery not reporting charge */
160 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
161 battery->capacity_now == 0)
162 return 0;
163
164 /* good batteries update full_charge as the batteries degrade */
165 if (battery->full_charge_capacity == battery->capacity_now)
166 return 1;
167
168 /* fallback to using design values for broken batteries */
169 if (battery->design_capacity == battery->capacity_now)
170 return 1;
171
172 /* we don't do any sort of metric based on percentages */
173 return 0;
174}
175
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400176static int acpi_battery_get_property(struct power_supply *psy,
177 enum power_supply_property psp,
178 union power_supply_propval *val)
179{
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200180 int ret = 0;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400181 struct acpi_battery *battery = to_acpi_battery(psy);
182
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300183 if (acpi_battery_present(battery)) {
184 /* run battery update only if it is present */
185 acpi_battery_get_state(battery);
186 } else if (psp != POWER_SUPPLY_PROP_PRESENT)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400187 return -ENODEV;
188 switch (psp) {
189 case POWER_SUPPLY_PROP_STATUS:
190 if (battery->state & 0x01)
191 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
192 else if (battery->state & 0x02)
193 val->intval = POWER_SUPPLY_STATUS_CHARGING;
Richard Hughes56f382a2009-01-25 15:05:50 +0000194 else if (acpi_battery_is_charged(battery))
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400195 val->intval = POWER_SUPPLY_STATUS_FULL;
Roland Dreier4c41d3a2007-11-07 15:09:09 -0800196 else
197 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400198 break;
199 case POWER_SUPPLY_PROP_PRESENT:
200 val->intval = acpi_battery_present(battery);
201 break;
202 case POWER_SUPPLY_PROP_TECHNOLOGY:
203 val->intval = acpi_battery_technology(battery);
204 break;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400205 case POWER_SUPPLY_PROP_CYCLE_COUNT:
206 val->intval = battery->cycle_count;
207 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400208 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200209 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
210 ret = -ENODEV;
211 else
212 val->intval = battery->design_voltage * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400213 break;
214 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200215 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
216 ret = -ENODEV;
217 else
218 val->intval = battery->voltage_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400219 break;
220 case POWER_SUPPLY_PROP_CURRENT_NOW:
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400221 case POWER_SUPPLY_PROP_POWER_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200222 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
223 ret = -ENODEV;
224 else
225 val->intval = battery->rate_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400226 break;
227 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
228 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200229 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
230 ret = -ENODEV;
231 else
232 val->intval = battery->design_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400233 break;
234 case POWER_SUPPLY_PROP_CHARGE_FULL:
235 case POWER_SUPPLY_PROP_ENERGY_FULL:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200236 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
237 ret = -ENODEV;
238 else
239 val->intval = battery->full_charge_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400240 break;
241 case POWER_SUPPLY_PROP_CHARGE_NOW:
242 case POWER_SUPPLY_PROP_ENERGY_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200243 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
244 ret = -ENODEV;
245 else
246 val->intval = battery->capacity_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400247 break;
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700248 case POWER_SUPPLY_PROP_CAPACITY:
249 if (battery->capacity_now && battery->full_charge_capacity)
250 val->intval = battery->capacity_now * 100/
251 battery->full_charge_capacity;
252 else
253 val->intval = 0;
254 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400255 case POWER_SUPPLY_PROP_MODEL_NAME:
256 val->strval = battery->model_number;
257 break;
258 case POWER_SUPPLY_PROP_MANUFACTURER:
259 val->strval = battery->oem_info;
260 break;
maximilian attems7c2670b2008-01-22 18:46:50 +0100261 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
262 val->strval = battery->serial_number;
263 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400264 default:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200265 ret = -EINVAL;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400266 }
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200267 return ret;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400268}
269
270static enum power_supply_property charge_battery_props[] = {
271 POWER_SUPPLY_PROP_STATUS,
272 POWER_SUPPLY_PROP_PRESENT,
273 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400274 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400275 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
276 POWER_SUPPLY_PROP_VOLTAGE_NOW,
277 POWER_SUPPLY_PROP_CURRENT_NOW,
278 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
279 POWER_SUPPLY_PROP_CHARGE_FULL,
280 POWER_SUPPLY_PROP_CHARGE_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700281 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400282 POWER_SUPPLY_PROP_MODEL_NAME,
283 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100284 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400285};
286
287static enum power_supply_property energy_battery_props[] = {
288 POWER_SUPPLY_PROP_STATUS,
289 POWER_SUPPLY_PROP_PRESENT,
290 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400291 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400292 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
293 POWER_SUPPLY_PROP_VOLTAGE_NOW,
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400294 POWER_SUPPLY_PROP_POWER_NOW,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400295 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
296 POWER_SUPPLY_PROP_ENERGY_FULL,
297 POWER_SUPPLY_PROP_ENERGY_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700298 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400299 POWER_SUPPLY_PROP_MODEL_NAME,
300 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100301 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400302};
303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304/* --------------------------------------------------------------------------
305 Battery Management
306 -------------------------------------------------------------------------- */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400307struct acpi_offsets {
308 size_t offset; /* offset inside struct acpi_sbs_battery */
309 u8 mode; /* int or string? */
310};
311
312static struct acpi_offsets state_offsets[] = {
313 {offsetof(struct acpi_battery, state), 0},
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400314 {offsetof(struct acpi_battery, rate_now), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400315 {offsetof(struct acpi_battery, capacity_now), 0},
316 {offsetof(struct acpi_battery, voltage_now), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400317};
318
319static struct acpi_offsets info_offsets[] = {
320 {offsetof(struct acpi_battery, power_unit), 0},
321 {offsetof(struct acpi_battery, design_capacity), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400322 {offsetof(struct acpi_battery, full_charge_capacity), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400323 {offsetof(struct acpi_battery, technology), 0},
324 {offsetof(struct acpi_battery, design_voltage), 0},
325 {offsetof(struct acpi_battery, design_capacity_warning), 0},
326 {offsetof(struct acpi_battery, design_capacity_low), 0},
327 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
328 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
329 {offsetof(struct acpi_battery, model_number), 1},
330 {offsetof(struct acpi_battery, serial_number), 1},
331 {offsetof(struct acpi_battery, type), 1},
332 {offsetof(struct acpi_battery, oem_info), 1},
333};
334
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400335static struct acpi_offsets extended_info_offsets[] = {
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200336 {offsetof(struct acpi_battery, revision), 0},
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400337 {offsetof(struct acpi_battery, power_unit), 0},
338 {offsetof(struct acpi_battery, design_capacity), 0},
339 {offsetof(struct acpi_battery, full_charge_capacity), 0},
340 {offsetof(struct acpi_battery, technology), 0},
341 {offsetof(struct acpi_battery, design_voltage), 0},
342 {offsetof(struct acpi_battery, design_capacity_warning), 0},
343 {offsetof(struct acpi_battery, design_capacity_low), 0},
344 {offsetof(struct acpi_battery, cycle_count), 0},
345 {offsetof(struct acpi_battery, measurement_accuracy), 0},
346 {offsetof(struct acpi_battery, max_sampling_time), 0},
347 {offsetof(struct acpi_battery, min_sampling_time), 0},
348 {offsetof(struct acpi_battery, max_averaging_interval), 0},
349 {offsetof(struct acpi_battery, min_averaging_interval), 0},
350 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
351 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
352 {offsetof(struct acpi_battery, model_number), 1},
353 {offsetof(struct acpi_battery, serial_number), 1},
354 {offsetof(struct acpi_battery, type), 1},
355 {offsetof(struct acpi_battery, oem_info), 1},
356};
357
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400358static int extract_package(struct acpi_battery *battery,
359 union acpi_object *package,
360 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300361{
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300362 int i;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400363 union acpi_object *element;
364 if (package->type != ACPI_TYPE_PACKAGE)
365 return -EFAULT;
366 for (i = 0; i < num; ++i) {
367 if (package->package.count <= i)
368 return -EFAULT;
369 element = &package->package.elements[i];
370 if (offsets[i].mode) {
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300371 u8 *ptr = (u8 *)battery + offsets[i].offset;
372 if (element->type == ACPI_TYPE_STRING ||
373 element->type == ACPI_TYPE_BUFFER)
374 strncpy(ptr, element->string.pointer, 32);
375 else if (element->type == ACPI_TYPE_INTEGER) {
376 strncpy(ptr, (u8 *)&element->integer.value,
Lin Ming439913f2010-01-28 10:53:19 +0800377 sizeof(u64));
378 ptr[sizeof(u64)] = 0;
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400379 } else
380 *ptr = 0; /* don't have value */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400381 } else {
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400382 int *x = (int *)((u8 *)battery + offsets[i].offset);
383 *x = (element->type == ACPI_TYPE_INTEGER) ?
384 element->integer.value : -1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300385 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300386 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300387 return 0;
388}
389
390static int acpi_battery_get_status(struct acpi_battery *battery)
391{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400392 if (acpi_bus_get_status(battery->device)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300393 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
394 return -ENODEV;
395 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400396 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300397}
398
399static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400401 int result = -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400402 acpi_status status = 0;
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000403 char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ?
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400404 "_BIX" : "_BIF";
405
Len Brown4be44fc2005-08-05 00:44:28 -0400406 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300408 if (!acpi_battery_present(battery))
409 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400410 mutex_lock(&battery->lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400411 status = acpi_evaluate_object(battery->device->handle, name,
412 NULL, &buffer);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400413 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 if (ACPI_FAILURE(status)) {
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400416 ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
Patrick Mocheld550d982006-06-27 00:41:40 -0400417 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400419 if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
420 result = extract_package(battery, buffer.pointer,
421 extended_info_offsets,
422 ARRAY_SIZE(extended_info_offsets));
423 else
424 result = extract_package(battery, buffer.pointer,
425 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400426 kfree(buffer.pointer);
Zhang Rui557d5862010-10-22 10:02:06 +0800427 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
428 battery->full_charge_capacity = battery->design_capacity;
Kamil Iskra4000e622012-11-16 22:28:58 +0100429 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
430 battery->power_unit && battery->design_voltage) {
431 battery->design_capacity = battery->design_capacity *
432 10000 / battery->design_voltage;
433 battery->full_charge_capacity = battery->full_charge_capacity *
434 10000 / battery->design_voltage;
435 battery->design_capacity_warning =
436 battery->design_capacity_warning *
437 10000 / battery->design_voltage;
438 /* Curiously, design_capacity_low, unlike the rest of them,
439 is correct. */
440 /* capacity_granularity_* equal 1 on the systems tested, so
441 it's impossible to tell if they would need an adjustment
442 or not if their values were higher. */
443 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400444 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445}
446
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300447static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
Len Brown4be44fc2005-08-05 00:44:28 -0400449 int result = 0;
450 acpi_status status = 0;
451 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300453 if (!acpi_battery_present(battery))
454 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400456 if (battery->update_time &&
457 time_before(jiffies, battery->update_time +
458 msecs_to_jiffies(cache_time)))
459 return 0;
460
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400461 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400462 status = acpi_evaluate_object(battery->device->handle, "_BST",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400463 NULL, &buffer);
464 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400465
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400467 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400468 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400470
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400471 result = extract_package(battery, buffer.pointer,
472 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400473 battery->update_time = jiffies;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400474 kfree(buffer.pointer);
Hector Martinbc76f902009-08-06 15:57:48 -0700475
Lan Tianyu55003b22011-06-30 11:33:12 +0800476 /* For buggy DSDTs that report negative 16-bit values for either
477 * charging or discharging current and/or report 0 as 65536
478 * due to bad math.
479 */
480 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
481 battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
482 (s16)(battery->rate_now) < 0) {
Hector Martinbc76f902009-08-06 15:57:48 -0700483 battery->rate_now = abs((s16)battery->rate_now);
Lan Tianyu55003b22011-06-30 11:33:12 +0800484 printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
485 " invalid.\n");
486 }
Hector Martinbc76f902009-08-06 15:57:48 -0700487
Zhang Rui557d5862010-10-22 10:02:06 +0800488 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
489 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
490 battery->capacity_now = (battery->capacity_now *
491 battery->full_charge_capacity) / 100;
Kamil Iskra4000e622012-11-16 22:28:58 +0100492 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
493 battery->power_unit && battery->design_voltage) {
494 battery->capacity_now = battery->capacity_now *
495 10000 / battery->design_voltage;
496 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400497 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400500static int acpi_battery_set_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
Len Brown4be44fc2005-08-05 00:44:28 -0400502 acpi_status status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400504 if (!acpi_battery_present(battery) ||
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400505 !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300506 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400508 mutex_lock(&battery->lock);
Jiang Liu0db98202013-06-29 00:24:39 +0800509 status = acpi_execute_simple_method(battery->device->handle, "_BTP",
510 battery->alarm);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400511 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400514 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400516 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm));
Patrick Mocheld550d982006-06-27 00:41:40 -0400517 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518}
519
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300520static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300522 /* See if alarms are supported, and if so, set default */
Jiang Liu952c63e2013-06-29 00:24:38 +0800523 if (!acpi_has_method(battery->device->handle, "_BTP")) {
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400524 clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400525 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 }
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400527 set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400528 if (!battery->alarm)
529 battery->alarm = battery->design_capacity_warning;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400530 return acpi_battery_set_alarm(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531}
532
Andrey Borzenkov508df922007-10-28 12:50:09 +0300533static ssize_t acpi_battery_alarm_show(struct device *dev,
534 struct device_attribute *attr,
535 char *buf)
536{
537 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
538 return sprintf(buf, "%d\n", battery->alarm * 1000);
539}
540
541static ssize_t acpi_battery_alarm_store(struct device *dev,
542 struct device_attribute *attr,
543 const char *buf, size_t count)
544{
545 unsigned long x;
546 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
547 if (sscanf(buf, "%ld\n", &x) == 1)
548 battery->alarm = x/1000;
549 if (acpi_battery_present(battery))
550 acpi_battery_set_alarm(battery);
551 return count;
552}
553
554static struct device_attribute alarm_attr = {
Parag Warudkar01e8ef12008-10-18 20:28:50 -0700555 .attr = {.name = "alarm", .mode = 0644},
Andrey Borzenkov508df922007-10-28 12:50:09 +0300556 .show = acpi_battery_alarm_show,
557 .store = acpi_battery_alarm_store,
558};
559
560static int sysfs_add_battery(struct acpi_battery *battery)
561{
562 int result;
563
Lan Tianyuae6f6182011-06-30 11:32:40 +0800564 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
Andrey Borzenkov508df922007-10-28 12:50:09 +0300565 battery->bat.properties = charge_battery_props;
566 battery->bat.num_properties =
567 ARRAY_SIZE(charge_battery_props);
568 } else {
569 battery->bat.properties = energy_battery_props;
570 battery->bat.num_properties =
571 ARRAY_SIZE(energy_battery_props);
572 }
573
574 battery->bat.name = acpi_device_bid(battery->device);
575 battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
576 battery->bat.get_property = acpi_battery_get_property;
577
578 result = power_supply_register(&battery->device->dev, &battery->bat);
579 if (result)
580 return result;
581 return device_create_file(battery->bat.dev, &alarm_attr);
582}
583
584static void sysfs_remove_battery(struct acpi_battery *battery)
585{
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300586 mutex_lock(&battery->sysfs_lock);
Lan Tianyu9c921c222011-06-30 11:34:12 +0800587 if (!battery->bat.dev) {
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300588 mutex_unlock(&battery->sysfs_lock);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300589 return;
Lan Tianyu9c921c222011-06-30 11:34:12 +0800590 }
591
Andrey Borzenkov508df922007-10-28 12:50:09 +0300592 device_remove_file(battery->bat.dev, &alarm_attr);
593 power_supply_unregister(&battery->bat);
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300594 battery->bat.dev = NULL;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300595 mutex_unlock(&battery->sysfs_lock);
Hector Martinbc76f902009-08-06 15:57:48 -0700596}
597
Kamil Iskra4000e622012-11-16 22:28:58 +0100598static void find_battery(const struct dmi_header *dm, void *private)
599{
600 struct acpi_battery *battery = (struct acpi_battery *)private;
601 /* Note: the hardcoded offsets below have been extracted from
602 the source code of dmidecode. */
603 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
604 const u8 *dmi_data = (const u8 *)(dm + 1);
605 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
606 if (dm->length >= 18)
607 dmi_capacity *= dmi_data[17];
608 if (battery->design_capacity * battery->design_voltage / 1000
609 != dmi_capacity &&
610 battery->design_capacity * 10 == dmi_capacity)
611 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
612 &battery->flags);
613 }
614}
615
Zhang Rui557d5862010-10-22 10:02:06 +0800616/*
617 * According to the ACPI spec, some kinds of primary batteries can
618 * report percentage battery remaining capacity directly to OS.
619 * In this case, it reports the Last Full Charged Capacity == 100
620 * and BatteryPresentRate == 0xFFFFFFFF.
621 *
622 * Now we found some battery reports percentage remaining capacity
623 * even if it's rechargeable.
624 * https://bugzilla.kernel.org/show_bug.cgi?id=15979
625 *
626 * Handle this correctly so that they won't break userspace.
627 */
Lan Tianyu7b786222011-06-30 11:33:27 +0800628static void acpi_battery_quirks(struct acpi_battery *battery)
Zhang Rui557d5862010-10-22 10:02:06 +0800629{
630 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000631 return;
Zhang Rui557d5862010-10-22 10:02:06 +0800632
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000633 if (battery->full_charge_capacity == 100 &&
634 battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
635 battery->capacity_now >= 0 && battery->capacity_now <= 100) {
Zhang Rui557d5862010-10-22 10:02:06 +0800636 set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
637 battery->full_charge_capacity = battery->design_capacity;
638 battery->capacity_now = (battery->capacity_now *
639 battery->full_charge_capacity) / 100;
640 }
Kamil Iskra4000e622012-11-16 22:28:58 +0100641
642 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000643 return;
Kamil Iskra4000e622012-11-16 22:28:58 +0100644
645 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
646 const char *s;
647 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
648 if (s && !strnicmp(s, "ThinkPad", 8)) {
649 dmi_walk(find_battery, battery);
650 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
651 &battery->flags) &&
652 battery->design_voltage) {
653 battery->design_capacity =
654 battery->design_capacity *
655 10000 / battery->design_voltage;
656 battery->full_charge_capacity =
657 battery->full_charge_capacity *
658 10000 / battery->design_voltage;
659 battery->design_capacity_warning =
660 battery->design_capacity_warning *
661 10000 / battery->design_voltage;
662 battery->capacity_now = battery->capacity_now *
663 10000 / battery->design_voltage;
664 }
665 }
666 }
Zhang Rui557d5862010-10-22 10:02:06 +0800667}
668
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400669static int acpi_battery_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500670{
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300671 int result, old_present = acpi_battery_present(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500672 result = acpi_battery_get_status(battery);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300673 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300674 return result;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300675 if (!acpi_battery_present(battery)) {
676 sysfs_remove_battery(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500677 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300678 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300679 }
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300680 if (!battery->update_time ||
681 old_present != acpi_battery_present(battery)) {
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500682 result = acpi_battery_get_info(battery);
683 if (result)
684 return result;
685 acpi_battery_init_alarm(battery);
686 }
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100687 if (!battery->bat.dev) {
688 result = sysfs_add_battery(battery);
689 if (result)
690 return result;
691 }
Zhang Rui557d5862010-10-22 10:02:06 +0800692 result = acpi_battery_get_state(battery);
Lan Tianyu7b786222011-06-30 11:33:27 +0800693 acpi_battery_quirks(battery);
Zhang Rui557d5862010-10-22 10:02:06 +0800694 return result;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500695}
696
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100697static void acpi_battery_refresh(struct acpi_battery *battery)
698{
Andy Whitcroftc5971452012-05-03 14:48:26 +0100699 int power_unit;
700
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100701 if (!battery->bat.dev)
702 return;
703
Andy Whitcroftc5971452012-05-03 14:48:26 +0100704 power_unit = battery->power_unit;
705
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100706 acpi_battery_get_info(battery);
Andy Whitcroftc5971452012-05-03 14:48:26 +0100707
708 if (power_unit == battery->power_unit)
709 return;
710
711 /* The battery has changed its reporting units. */
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100712 sysfs_remove_battery(battery);
713 sysfs_add_battery(battery);
714}
715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716/* --------------------------------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 Driver Interface
718 -------------------------------------------------------------------------- */
719
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600720static void acpi_battery_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721{
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600722 struct acpi_battery *battery = acpi_driver_data(device);
Zhang Rui153e5002010-07-07 09:11:57 +0800723 struct device *old;
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400726 return;
Zhang Rui153e5002010-07-07 09:11:57 +0800727 old = battery->bat.dev;
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100728 if (event == ACPI_BATTERY_NOTIFY_INFO)
729 acpi_battery_refresh(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400730 acpi_battery_update(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400731 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +0100732 dev_name(&device->dev), event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300733 acpi_battery_present(battery));
Justin P. Mattock2345baf2009-12-13 14:42:36 -0800734 /* acpi_battery_update could remove power_supply object */
Zhang Rui153e5002010-07-07 09:11:57 +0800735 if (old && battery->bat.dev)
Alan Jenkinsf79e1ce2009-06-30 14:36:16 +0000736 power_supply_changed(&battery->bat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
Kyle McMartin25be5822011-03-22 16:19:50 -0400739static int battery_notify(struct notifier_block *nb,
740 unsigned long mode, void *_unused)
741{
742 struct acpi_battery *battery = container_of(nb, struct acpi_battery,
743 pm_nb);
744 switch (mode) {
Lan Tianyud5a59112011-06-30 11:33:40 +0800745 case PM_POST_HIBERNATION:
Kyle McMartin25be5822011-03-22 16:19:50 -0400746 case PM_POST_SUSPEND:
Lan Tianyu6e17fb62011-06-30 11:33:58 +0800747 if (battery->bat.dev) {
748 sysfs_remove_battery(battery);
749 sysfs_add_battery(battery);
750 }
Kyle McMartin25be5822011-03-22 16:19:50 -0400751 break;
752 }
753
754 return 0;
755}
756
Len Brown4be44fc2005-08-05 00:44:28 -0400757static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758{
Len Brown4be44fc2005-08-05 00:44:28 -0400759 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400760 struct acpi_battery *battery = NULL;
Jiang Liu952c63e2013-06-29 00:24:38 +0800761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400763 return -EINVAL;
Burman Yan36bcbec2006-12-19 12:56:11 -0800764 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400766 return -ENOMEM;
Patrick Mochel145def82006-05-19 16:54:39 -0400767 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
769 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700770 device->driver_data = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400771 mutex_init(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300772 mutex_init(&battery->sysfs_lock);
Jiang Liu952c63e2013-06-29 00:24:38 +0800773 if (acpi_has_method(battery->device->handle, "_BIX"))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400774 set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100775 result = acpi_battery_update(battery);
776 if (result)
777 goto fail;
Kyle McMartin25be5822011-03-22 16:19:50 -0400778
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100779 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
780 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
781 device->status.battery_present ? "present" : "absent");
782
Kyle McMartin25be5822011-03-22 16:19:50 -0400783 battery->pm_nb.notifier_call = battery_notify;
784 register_pm_notifier(&battery->pm_nb);
785
Patrick Mocheld550d982006-06-27 00:41:40 -0400786 return result;
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100787
788fail:
789 sysfs_remove_battery(battery);
790 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300791 mutex_destroy(&battery->sysfs_lock);
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100792 kfree(battery);
793 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794}
795
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100796static int acpi_battery_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797{
Len Brown4be44fc2005-08-05 00:44:28 -0400798 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400801 return -EINVAL;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200802 battery = acpi_driver_data(device);
Kyle McMartin25be5822011-03-22 16:19:50 -0400803 unregister_pm_notifier(&battery->pm_nb);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300804 sysfs_remove_battery(battery);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400805 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300806 mutex_destroy(&battery->sysfs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 kfree(battery);
Patrick Mocheld550d982006-06-27 00:41:40 -0400808 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809}
810
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200811#ifdef CONFIG_PM_SLEEP
Jiri Kosina34c44152006-10-10 14:20:41 -0700812/* this is needed to learn about changes made in suspended state */
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200813static int acpi_battery_resume(struct device *dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700814{
815 struct acpi_battery *battery;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200816
817 if (!dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700818 return -EINVAL;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200819
820 battery = acpi_driver_data(to_acpi_device(dev));
821 if (!battery)
822 return -EINVAL;
823
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400824 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300825 acpi_battery_update(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300826 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700827}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200828#endif
Jiri Kosina34c44152006-10-10 14:20:41 -0700829
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200830static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
831
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400832static struct acpi_driver acpi_battery_driver = {
833 .name = "battery",
834 .class = ACPI_BATTERY_CLASS,
835 .ids = battery_device_ids,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600836 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400837 .ops = {
838 .add = acpi_battery_add,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400839 .remove = acpi_battery_remove,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600840 .notify = acpi_battery_notify,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400841 },
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200842 .drv.pm = &acpi_battery_pm,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400843};
844
Linus Torvaldsb0cbc862009-04-11 12:45:20 -0700845static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
Pavel Machek4d8316d2006-08-14 22:37:22 -0700847 if (acpi_disabled)
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500848 return;
Lan Tianyu1e2d9cd2013-10-11 09:54:08 +0800849 acpi_bus_register_driver(&acpi_battery_driver);
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500850}
851
852static int __init acpi_battery_init(void)
853{
854 async_schedule(acpi_battery_init_async, NULL);
Patrick Mocheld550d982006-06-27 00:41:40 -0400855 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
857
Len Brown4be44fc2005-08-05 00:44:28 -0400858static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 acpi_bus_unregister_driver(&acpi_battery_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861}
862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863module_init(acpi_battery_init);
864module_exit(acpi_battery_exit);