blob: e90ef8b96f266086ade6178162d3f87e2b3f6aa1 [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
Lv Zheng8b484632013-12-03 08:49:16 +080039#include <linux/acpi.h>
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040040#include <linux/power_supply.h>
41
Len Browna192a952009-07-28 16:45:54 -040042#define PREFIX "ACPI: "
43
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#define ACPI_BATTERY_CLASS "battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define ACPI_BATTERY_DEVICE_NAME "Battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define ACPI_BATTERY_NOTIFY_STATUS 0x80
49#define ACPI_BATTERY_NOTIFY_INFO 0x81
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040050#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Lan Tianyuae6f6182011-06-30 11:32:40 +080052/* Battery power unit: 0 means mW, 1 means mA */
53#define ACPI_BATTERY_POWER_UNIT_MA 1
54
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030056
Len Brownf52fd662007-02-12 22:42:12 -050057ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Len Brownf52fd662007-02-12 22:42:12 -050059MODULE_AUTHOR("Paul Diefenbaugh");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +040060MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
Len Brown7cda93e2007-02-12 23:50:02 -050061MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070062MODULE_LICENSE("GPL");
63
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040064static unsigned int cache_time = 1000;
65module_param(cache_time, uint, 0644);
66MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030067
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040068static const struct acpi_device_id battery_device_ids[] = {
69 {"PNP0C0A", 0},
70 {"", 0},
71};
72
73MODULE_DEVICE_TABLE(acpi, battery_device_ids);
74
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040075enum {
76 ACPI_BATTERY_ALARM_PRESENT,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040077 ACPI_BATTERY_XINFO_PRESENT,
Zhang Rui557d5862010-10-22 10:02:06 +080078 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
Kamil Iskra4000e622012-11-16 22:28:58 +010079 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
80 switches between mWh and mAh depending on whether the system
81 is running on battery or not. When mAh is the unit, most
82 reported values are incorrect and need to be adjusted by
83 10000/design_voltage. Verified on x201, t410, t410s, and x220.
84 Pre-2010 and 2012 models appear to always report in mWh and
85 are thus unaffected (tested with t42, t61, t500, x200, x300,
86 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
87 the 2011 models that fixes the issue (tested on x220 with a
88 post-1.29 BIOS), but as of Nov. 2012, no such update is
89 available for the 2010 models. */
90 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040091};
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040092
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040093struct acpi_battery {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040094 struct mutex lock;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +030095 struct mutex sysfs_lock;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040096 struct power_supply bat;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040097 struct acpi_device *device;
Kyle McMartin25be5822011-03-22 16:19:50 -040098 struct notifier_block pm_nb;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040099 unsigned long update_time;
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200100 int revision;
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400101 int rate_now;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400102 int capacity_now;
103 int voltage_now;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400104 int design_capacity;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400105 int full_charge_capacity;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400106 int technology;
107 int design_voltage;
108 int design_capacity_warning;
109 int design_capacity_low;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400110 int cycle_count;
111 int measurement_accuracy;
112 int max_sampling_time;
113 int min_sampling_time;
114 int max_averaging_interval;
115 int min_averaging_interval;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400116 int capacity_granularity_1;
117 int capacity_granularity_2;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400118 int alarm;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400119 char model_number[32];
120 char serial_number[32];
121 char type[32];
122 char oem_info[32];
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400123 int state;
124 int power_unit;
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400125 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126};
127
Phil Carmody497888c2011-07-14 15:07:13 +0300128#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400129
Andy Shevchenkoefd941f2013-03-11 09:17:06 +0000130static inline int acpi_battery_present(struct acpi_battery *battery)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400131{
132 return battery->device->status.battery_present;
133}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400134
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400135static int acpi_battery_technology(struct acpi_battery *battery)
136{
137 if (!strcasecmp("NiCd", battery->type))
138 return POWER_SUPPLY_TECHNOLOGY_NiCd;
139 if (!strcasecmp("NiMH", battery->type))
140 return POWER_SUPPLY_TECHNOLOGY_NiMH;
141 if (!strcasecmp("LION", battery->type))
142 return POWER_SUPPLY_TECHNOLOGY_LION;
Andrey Borzenkovad40e682007-11-10 20:02:49 +0300143 if (!strncasecmp("LI-ION", battery->type, 6))
Alexey Starikovskiy0bde7ee2007-10-28 15:33:10 +0300144 return POWER_SUPPLY_TECHNOLOGY_LION;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400145 if (!strcasecmp("LiP", battery->type))
146 return POWER_SUPPLY_TECHNOLOGY_LIPO;
147 return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
148}
149
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300150static int acpi_battery_get_state(struct acpi_battery *battery);
Alexey Starikovskiyb19073a2007-10-25 17:10:47 -0400151
Richard Hughes56f382a2009-01-25 15:05:50 +0000152static int acpi_battery_is_charged(struct acpi_battery *battery)
153{
154 /* either charging or discharging */
155 if (battery->state != 0)
156 return 0;
157
158 /* battery not reporting charge */
159 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
160 battery->capacity_now == 0)
161 return 0;
162
163 /* good batteries update full_charge as the batteries degrade */
164 if (battery->full_charge_capacity == battery->capacity_now)
165 return 1;
166
167 /* fallback to using design values for broken batteries */
168 if (battery->design_capacity == battery->capacity_now)
169 return 1;
170
171 /* we don't do any sort of metric based on percentages */
172 return 0;
173}
174
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400175static int acpi_battery_get_property(struct power_supply *psy,
176 enum power_supply_property psp,
177 union power_supply_propval *val)
178{
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200179 int ret = 0;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400180 struct acpi_battery *battery = to_acpi_battery(psy);
181
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300182 if (acpi_battery_present(battery)) {
183 /* run battery update only if it is present */
184 acpi_battery_get_state(battery);
185 } else if (psp != POWER_SUPPLY_PROP_PRESENT)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400186 return -ENODEV;
187 switch (psp) {
188 case POWER_SUPPLY_PROP_STATUS:
189 if (battery->state & 0x01)
190 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
191 else if (battery->state & 0x02)
192 val->intval = POWER_SUPPLY_STATUS_CHARGING;
Richard Hughes56f382a2009-01-25 15:05:50 +0000193 else if (acpi_battery_is_charged(battery))
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400194 val->intval = POWER_SUPPLY_STATUS_FULL;
Roland Dreier4c41d3a2007-11-07 15:09:09 -0800195 else
196 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400197 break;
198 case POWER_SUPPLY_PROP_PRESENT:
199 val->intval = acpi_battery_present(battery);
200 break;
201 case POWER_SUPPLY_PROP_TECHNOLOGY:
202 val->intval = acpi_battery_technology(battery);
203 break;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400204 case POWER_SUPPLY_PROP_CYCLE_COUNT:
205 val->intval = battery->cycle_count;
206 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400207 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200208 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
209 ret = -ENODEV;
210 else
211 val->intval = battery->design_voltage * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400212 break;
213 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200214 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
215 ret = -ENODEV;
216 else
217 val->intval = battery->voltage_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400218 break;
219 case POWER_SUPPLY_PROP_CURRENT_NOW:
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400220 case POWER_SUPPLY_PROP_POWER_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200221 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
222 ret = -ENODEV;
223 else
224 val->intval = battery->rate_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400225 break;
226 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
227 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200228 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
229 ret = -ENODEV;
230 else
231 val->intval = battery->design_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400232 break;
233 case POWER_SUPPLY_PROP_CHARGE_FULL:
234 case POWER_SUPPLY_PROP_ENERGY_FULL:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200235 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
236 ret = -ENODEV;
237 else
238 val->intval = battery->full_charge_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400239 break;
240 case POWER_SUPPLY_PROP_CHARGE_NOW:
241 case POWER_SUPPLY_PROP_ENERGY_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200242 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
243 ret = -ENODEV;
244 else
245 val->intval = battery->capacity_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400246 break;
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700247 case POWER_SUPPLY_PROP_CAPACITY:
248 if (battery->capacity_now && battery->full_charge_capacity)
249 val->intval = battery->capacity_now * 100/
250 battery->full_charge_capacity;
251 else
252 val->intval = 0;
253 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400254 case POWER_SUPPLY_PROP_MODEL_NAME:
255 val->strval = battery->model_number;
256 break;
257 case POWER_SUPPLY_PROP_MANUFACTURER:
258 val->strval = battery->oem_info;
259 break;
maximilian attems7c2670b2008-01-22 18:46:50 +0100260 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
261 val->strval = battery->serial_number;
262 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400263 default:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200264 ret = -EINVAL;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400265 }
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200266 return ret;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400267}
268
269static enum power_supply_property charge_battery_props[] = {
270 POWER_SUPPLY_PROP_STATUS,
271 POWER_SUPPLY_PROP_PRESENT,
272 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400273 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400274 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
275 POWER_SUPPLY_PROP_VOLTAGE_NOW,
276 POWER_SUPPLY_PROP_CURRENT_NOW,
277 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
278 POWER_SUPPLY_PROP_CHARGE_FULL,
279 POWER_SUPPLY_PROP_CHARGE_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700280 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400281 POWER_SUPPLY_PROP_MODEL_NAME,
282 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100283 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400284};
285
286static enum power_supply_property energy_battery_props[] = {
287 POWER_SUPPLY_PROP_STATUS,
288 POWER_SUPPLY_PROP_PRESENT,
289 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400290 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400291 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
292 POWER_SUPPLY_PROP_VOLTAGE_NOW,
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400293 POWER_SUPPLY_PROP_POWER_NOW,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400294 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
295 POWER_SUPPLY_PROP_ENERGY_FULL,
296 POWER_SUPPLY_PROP_ENERGY_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700297 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400298 POWER_SUPPLY_PROP_MODEL_NAME,
299 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100300 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400301};
302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303/* --------------------------------------------------------------------------
304 Battery Management
305 -------------------------------------------------------------------------- */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400306struct acpi_offsets {
307 size_t offset; /* offset inside struct acpi_sbs_battery */
308 u8 mode; /* int or string? */
309};
310
311static struct acpi_offsets state_offsets[] = {
312 {offsetof(struct acpi_battery, state), 0},
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400313 {offsetof(struct acpi_battery, rate_now), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400314 {offsetof(struct acpi_battery, capacity_now), 0},
315 {offsetof(struct acpi_battery, voltage_now), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400316};
317
318static struct acpi_offsets info_offsets[] = {
319 {offsetof(struct acpi_battery, power_unit), 0},
320 {offsetof(struct acpi_battery, design_capacity), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400321 {offsetof(struct acpi_battery, full_charge_capacity), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400322 {offsetof(struct acpi_battery, technology), 0},
323 {offsetof(struct acpi_battery, design_voltage), 0},
324 {offsetof(struct acpi_battery, design_capacity_warning), 0},
325 {offsetof(struct acpi_battery, design_capacity_low), 0},
326 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
327 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
328 {offsetof(struct acpi_battery, model_number), 1},
329 {offsetof(struct acpi_battery, serial_number), 1},
330 {offsetof(struct acpi_battery, type), 1},
331 {offsetof(struct acpi_battery, oem_info), 1},
332};
333
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400334static struct acpi_offsets extended_info_offsets[] = {
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200335 {offsetof(struct acpi_battery, revision), 0},
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400336 {offsetof(struct acpi_battery, power_unit), 0},
337 {offsetof(struct acpi_battery, design_capacity), 0},
338 {offsetof(struct acpi_battery, full_charge_capacity), 0},
339 {offsetof(struct acpi_battery, technology), 0},
340 {offsetof(struct acpi_battery, design_voltage), 0},
341 {offsetof(struct acpi_battery, design_capacity_warning), 0},
342 {offsetof(struct acpi_battery, design_capacity_low), 0},
343 {offsetof(struct acpi_battery, cycle_count), 0},
344 {offsetof(struct acpi_battery, measurement_accuracy), 0},
345 {offsetof(struct acpi_battery, max_sampling_time), 0},
346 {offsetof(struct acpi_battery, min_sampling_time), 0},
347 {offsetof(struct acpi_battery, max_averaging_interval), 0},
348 {offsetof(struct acpi_battery, min_averaging_interval), 0},
349 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
350 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
351 {offsetof(struct acpi_battery, model_number), 1},
352 {offsetof(struct acpi_battery, serial_number), 1},
353 {offsetof(struct acpi_battery, type), 1},
354 {offsetof(struct acpi_battery, oem_info), 1},
355};
356
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400357static int extract_package(struct acpi_battery *battery,
358 union acpi_object *package,
359 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300360{
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300361 int i;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400362 union acpi_object *element;
363 if (package->type != ACPI_TYPE_PACKAGE)
364 return -EFAULT;
365 for (i = 0; i < num; ++i) {
366 if (package->package.count <= i)
367 return -EFAULT;
368 element = &package->package.elements[i];
369 if (offsets[i].mode) {
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300370 u8 *ptr = (u8 *)battery + offsets[i].offset;
371 if (element->type == ACPI_TYPE_STRING ||
372 element->type == ACPI_TYPE_BUFFER)
373 strncpy(ptr, element->string.pointer, 32);
374 else if (element->type == ACPI_TYPE_INTEGER) {
375 strncpy(ptr, (u8 *)&element->integer.value,
Lin Ming439913f2010-01-28 10:53:19 +0800376 sizeof(u64));
377 ptr[sizeof(u64)] = 0;
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400378 } else
379 *ptr = 0; /* don't have value */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400380 } else {
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400381 int *x = (int *)((u8 *)battery + offsets[i].offset);
382 *x = (element->type == ACPI_TYPE_INTEGER) ?
383 element->integer.value : -1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300384 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300385 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300386 return 0;
387}
388
389static int acpi_battery_get_status(struct acpi_battery *battery)
390{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400391 if (acpi_bus_get_status(battery->device)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300392 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
393 return -ENODEV;
394 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400395 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300396}
397
398static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400400 int result = -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400401 acpi_status status = 0;
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000402 char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ?
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400403 "_BIX" : "_BIF";
404
Len Brown4be44fc2005-08-05 00:44:28 -0400405 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300407 if (!acpi_battery_present(battery))
408 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400409 mutex_lock(&battery->lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400410 status = acpi_evaluate_object(battery->device->handle, name,
411 NULL, &buffer);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400412 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 if (ACPI_FAILURE(status)) {
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400415 ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
Patrick Mocheld550d982006-06-27 00:41:40 -0400416 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 }
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400418 if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
419 result = extract_package(battery, buffer.pointer,
420 extended_info_offsets,
421 ARRAY_SIZE(extended_info_offsets));
422 else
423 result = extract_package(battery, buffer.pointer,
424 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400425 kfree(buffer.pointer);
Zhang Rui557d5862010-10-22 10:02:06 +0800426 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
427 battery->full_charge_capacity = battery->design_capacity;
Kamil Iskra4000e622012-11-16 22:28:58 +0100428 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
429 battery->power_unit && battery->design_voltage) {
430 battery->design_capacity = battery->design_capacity *
431 10000 / battery->design_voltage;
432 battery->full_charge_capacity = battery->full_charge_capacity *
433 10000 / battery->design_voltage;
434 battery->design_capacity_warning =
435 battery->design_capacity_warning *
436 10000 / battery->design_voltage;
437 /* Curiously, design_capacity_low, unlike the rest of them,
438 is correct. */
439 /* capacity_granularity_* equal 1 on the systems tested, so
440 it's impossible to tell if they would need an adjustment
441 or not if their values were higher. */
442 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400443 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444}
445
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300446static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
Len Brown4be44fc2005-08-05 00:44:28 -0400448 int result = 0;
449 acpi_status status = 0;
450 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300452 if (!acpi_battery_present(battery))
453 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400455 if (battery->update_time &&
456 time_before(jiffies, battery->update_time +
457 msecs_to_jiffies(cache_time)))
458 return 0;
459
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400460 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400461 status = acpi_evaluate_object(battery->device->handle, "_BST",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400462 NULL, &buffer);
463 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400466 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400467 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400469
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400470 result = extract_package(battery, buffer.pointer,
471 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400472 battery->update_time = jiffies;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400473 kfree(buffer.pointer);
Hector Martinbc76f902009-08-06 15:57:48 -0700474
Lan Tianyu55003b22011-06-30 11:33:12 +0800475 /* For buggy DSDTs that report negative 16-bit values for either
476 * charging or discharging current and/or report 0 as 65536
477 * due to bad math.
478 */
479 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
480 battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
481 (s16)(battery->rate_now) < 0) {
Hector Martinbc76f902009-08-06 15:57:48 -0700482 battery->rate_now = abs((s16)battery->rate_now);
Lan Tianyu55003b22011-06-30 11:33:12 +0800483 printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
484 " invalid.\n");
485 }
Hector Martinbc76f902009-08-06 15:57:48 -0700486
Zhang Rui557d5862010-10-22 10:02:06 +0800487 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
488 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
489 battery->capacity_now = (battery->capacity_now *
490 battery->full_charge_capacity) / 100;
Kamil Iskra4000e622012-11-16 22:28:58 +0100491 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
492 battery->power_unit && battery->design_voltage) {
493 battery->capacity_now = battery->capacity_now *
494 10000 / battery->design_voltage;
495 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400496 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497}
498
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400499static int acpi_battery_set_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Len Brown4be44fc2005-08-05 00:44:28 -0400501 acpi_status status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400503 if (!acpi_battery_present(battery) ||
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400504 !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300505 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400507 mutex_lock(&battery->lock);
Jiang Liu0db98202013-06-29 00:24:39 +0800508 status = acpi_execute_simple_method(battery->device->handle, "_BTP",
509 battery->alarm);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400510 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400513 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400515 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm));
Patrick Mocheld550d982006-06-27 00:41:40 -0400516 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300519static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300521 /* See if alarms are supported, and if so, set default */
Jiang Liu952c63e2013-06-29 00:24:38 +0800522 if (!acpi_has_method(battery->device->handle, "_BTP")) {
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400523 clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400524 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400526 set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400527 if (!battery->alarm)
528 battery->alarm = battery->design_capacity_warning;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400529 return acpi_battery_set_alarm(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
Andrey Borzenkov508df922007-10-28 12:50:09 +0300532static ssize_t acpi_battery_alarm_show(struct device *dev,
533 struct device_attribute *attr,
534 char *buf)
535{
536 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
537 return sprintf(buf, "%d\n", battery->alarm * 1000);
538}
539
540static ssize_t acpi_battery_alarm_store(struct device *dev,
541 struct device_attribute *attr,
542 const char *buf, size_t count)
543{
544 unsigned long x;
545 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
546 if (sscanf(buf, "%ld\n", &x) == 1)
547 battery->alarm = x/1000;
548 if (acpi_battery_present(battery))
549 acpi_battery_set_alarm(battery);
550 return count;
551}
552
553static struct device_attribute alarm_attr = {
Parag Warudkar01e8ef12008-10-18 20:28:50 -0700554 .attr = {.name = "alarm", .mode = 0644},
Andrey Borzenkov508df922007-10-28 12:50:09 +0300555 .show = acpi_battery_alarm_show,
556 .store = acpi_battery_alarm_store,
557};
558
559static int sysfs_add_battery(struct acpi_battery *battery)
560{
561 int result;
562
Lan Tianyuae6f6182011-06-30 11:32:40 +0800563 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
Andrey Borzenkov508df922007-10-28 12:50:09 +0300564 battery->bat.properties = charge_battery_props;
565 battery->bat.num_properties =
566 ARRAY_SIZE(charge_battery_props);
567 } else {
568 battery->bat.properties = energy_battery_props;
569 battery->bat.num_properties =
570 ARRAY_SIZE(energy_battery_props);
571 }
572
573 battery->bat.name = acpi_device_bid(battery->device);
574 battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
575 battery->bat.get_property = acpi_battery_get_property;
576
577 result = power_supply_register(&battery->device->dev, &battery->bat);
578 if (result)
579 return result;
580 return device_create_file(battery->bat.dev, &alarm_attr);
581}
582
583static void sysfs_remove_battery(struct acpi_battery *battery)
584{
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300585 mutex_lock(&battery->sysfs_lock);
Lan Tianyu9c921c222011-06-30 11:34:12 +0800586 if (!battery->bat.dev) {
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300587 mutex_unlock(&battery->sysfs_lock);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300588 return;
Lan Tianyu9c921c222011-06-30 11:34:12 +0800589 }
590
Andrey Borzenkov508df922007-10-28 12:50:09 +0300591 device_remove_file(battery->bat.dev, &alarm_attr);
592 power_supply_unregister(&battery->bat);
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300593 battery->bat.dev = NULL;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300594 mutex_unlock(&battery->sysfs_lock);
Hector Martinbc76f902009-08-06 15:57:48 -0700595}
596
Kamil Iskra4000e622012-11-16 22:28:58 +0100597static void find_battery(const struct dmi_header *dm, void *private)
598{
599 struct acpi_battery *battery = (struct acpi_battery *)private;
600 /* Note: the hardcoded offsets below have been extracted from
601 the source code of dmidecode. */
602 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
603 const u8 *dmi_data = (const u8 *)(dm + 1);
604 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
605 if (dm->length >= 18)
606 dmi_capacity *= dmi_data[17];
607 if (battery->design_capacity * battery->design_voltage / 1000
608 != dmi_capacity &&
609 battery->design_capacity * 10 == dmi_capacity)
610 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
611 &battery->flags);
612 }
613}
614
Zhang Rui557d5862010-10-22 10:02:06 +0800615/*
616 * According to the ACPI spec, some kinds of primary batteries can
617 * report percentage battery remaining capacity directly to OS.
618 * In this case, it reports the Last Full Charged Capacity == 100
619 * and BatteryPresentRate == 0xFFFFFFFF.
620 *
621 * Now we found some battery reports percentage remaining capacity
622 * even if it's rechargeable.
623 * https://bugzilla.kernel.org/show_bug.cgi?id=15979
624 *
625 * Handle this correctly so that they won't break userspace.
626 */
Lan Tianyu7b786222011-06-30 11:33:27 +0800627static void acpi_battery_quirks(struct acpi_battery *battery)
Zhang Rui557d5862010-10-22 10:02:06 +0800628{
629 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000630 return;
Zhang Rui557d5862010-10-22 10:02:06 +0800631
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000632 if (battery->full_charge_capacity == 100 &&
633 battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
634 battery->capacity_now >= 0 && battery->capacity_now <= 100) {
Zhang Rui557d5862010-10-22 10:02:06 +0800635 set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
636 battery->full_charge_capacity = battery->design_capacity;
637 battery->capacity_now = (battery->capacity_now *
638 battery->full_charge_capacity) / 100;
639 }
Kamil Iskra4000e622012-11-16 22:28:58 +0100640
641 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000642 return;
Kamil Iskra4000e622012-11-16 22:28:58 +0100643
644 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
645 const char *s;
646 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
647 if (s && !strnicmp(s, "ThinkPad", 8)) {
648 dmi_walk(find_battery, battery);
649 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
650 &battery->flags) &&
651 battery->design_voltage) {
652 battery->design_capacity =
653 battery->design_capacity *
654 10000 / battery->design_voltage;
655 battery->full_charge_capacity =
656 battery->full_charge_capacity *
657 10000 / battery->design_voltage;
658 battery->design_capacity_warning =
659 battery->design_capacity_warning *
660 10000 / battery->design_voltage;
661 battery->capacity_now = battery->capacity_now *
662 10000 / battery->design_voltage;
663 }
664 }
665 }
Zhang Rui557d5862010-10-22 10:02:06 +0800666}
667
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400668static int acpi_battery_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500669{
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300670 int result, old_present = acpi_battery_present(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500671 result = acpi_battery_get_status(battery);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300672 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300673 return result;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300674 if (!acpi_battery_present(battery)) {
675 sysfs_remove_battery(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500676 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300677 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300678 }
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300679 if (!battery->update_time ||
680 old_present != acpi_battery_present(battery)) {
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500681 result = acpi_battery_get_info(battery);
682 if (result)
683 return result;
684 acpi_battery_init_alarm(battery);
685 }
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100686 if (!battery->bat.dev) {
687 result = sysfs_add_battery(battery);
688 if (result)
689 return result;
690 }
Zhang Rui557d5862010-10-22 10:02:06 +0800691 result = acpi_battery_get_state(battery);
Lan Tianyu7b786222011-06-30 11:33:27 +0800692 acpi_battery_quirks(battery);
Zhang Rui557d5862010-10-22 10:02:06 +0800693 return result;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500694}
695
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100696static void acpi_battery_refresh(struct acpi_battery *battery)
697{
Andy Whitcroftc5971452012-05-03 14:48:26 +0100698 int power_unit;
699
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100700 if (!battery->bat.dev)
701 return;
702
Andy Whitcroftc5971452012-05-03 14:48:26 +0100703 power_unit = battery->power_unit;
704
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100705 acpi_battery_get_info(battery);
Andy Whitcroftc5971452012-05-03 14:48:26 +0100706
707 if (power_unit == battery->power_unit)
708 return;
709
710 /* The battery has changed its reporting units. */
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100711 sysfs_remove_battery(battery);
712 sysfs_add_battery(battery);
713}
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715/* --------------------------------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 Driver Interface
717 -------------------------------------------------------------------------- */
718
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600719static void acpi_battery_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720{
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600721 struct acpi_battery *battery = acpi_driver_data(device);
Zhang Rui153e5002010-07-07 09:11:57 +0800722 struct device *old;
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400725 return;
Zhang Rui153e5002010-07-07 09:11:57 +0800726 old = battery->bat.dev;
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100727 if (event == ACPI_BATTERY_NOTIFY_INFO)
728 acpi_battery_refresh(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400729 acpi_battery_update(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400730 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +0100731 dev_name(&device->dev), event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300732 acpi_battery_present(battery));
Justin P. Mattock2345baf2009-12-13 14:42:36 -0800733 /* acpi_battery_update could remove power_supply object */
Zhang Rui153e5002010-07-07 09:11:57 +0800734 if (old && battery->bat.dev)
Alan Jenkinsf79e1ce2009-06-30 14:36:16 +0000735 power_supply_changed(&battery->bat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
Kyle McMartin25be5822011-03-22 16:19:50 -0400738static int battery_notify(struct notifier_block *nb,
739 unsigned long mode, void *_unused)
740{
741 struct acpi_battery *battery = container_of(nb, struct acpi_battery,
742 pm_nb);
743 switch (mode) {
Lan Tianyud5a59112011-06-30 11:33:40 +0800744 case PM_POST_HIBERNATION:
Kyle McMartin25be5822011-03-22 16:19:50 -0400745 case PM_POST_SUSPEND:
Lan Tianyu6e17fb62011-06-30 11:33:58 +0800746 if (battery->bat.dev) {
747 sysfs_remove_battery(battery);
748 sysfs_add_battery(battery);
749 }
Kyle McMartin25be5822011-03-22 16:19:50 -0400750 break;
751 }
752
753 return 0;
754}
755
Len Brown4be44fc2005-08-05 00:44:28 -0400756static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757{
Len Brown4be44fc2005-08-05 00:44:28 -0400758 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400759 struct acpi_battery *battery = NULL;
Jiang Liu952c63e2013-06-29 00:24:38 +0800760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400762 return -EINVAL;
Burman Yan36bcbec2006-12-19 12:56:11 -0800763 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400765 return -ENOMEM;
Patrick Mochel145def82006-05-19 16:54:39 -0400766 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
768 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700769 device->driver_data = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400770 mutex_init(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300771 mutex_init(&battery->sysfs_lock);
Jiang Liu952c63e2013-06-29 00:24:38 +0800772 if (acpi_has_method(battery->device->handle, "_BIX"))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400773 set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100774 result = acpi_battery_update(battery);
775 if (result)
776 goto fail;
Kyle McMartin25be5822011-03-22 16:19:50 -0400777
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100778 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
779 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
780 device->status.battery_present ? "present" : "absent");
781
Kyle McMartin25be5822011-03-22 16:19:50 -0400782 battery->pm_nb.notifier_call = battery_notify;
783 register_pm_notifier(&battery->pm_nb);
784
Patrick Mocheld550d982006-06-27 00:41:40 -0400785 return result;
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100786
787fail:
788 sysfs_remove_battery(battery);
789 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300790 mutex_destroy(&battery->sysfs_lock);
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100791 kfree(battery);
792 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793}
794
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100795static int acpi_battery_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
Len Brown4be44fc2005-08-05 00:44:28 -0400797 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400800 return -EINVAL;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200801 battery = acpi_driver_data(device);
Kyle McMartin25be5822011-03-22 16:19:50 -0400802 unregister_pm_notifier(&battery->pm_nb);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300803 sysfs_remove_battery(battery);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400804 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300805 mutex_destroy(&battery->sysfs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 kfree(battery);
Patrick Mocheld550d982006-06-27 00:41:40 -0400807 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200810#ifdef CONFIG_PM_SLEEP
Jiri Kosina34c44152006-10-10 14:20:41 -0700811/* this is needed to learn about changes made in suspended state */
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200812static int acpi_battery_resume(struct device *dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700813{
814 struct acpi_battery *battery;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200815
816 if (!dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700817 return -EINVAL;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200818
819 battery = acpi_driver_data(to_acpi_device(dev));
820 if (!battery)
821 return -EINVAL;
822
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400823 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300824 acpi_battery_update(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300825 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700826}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200827#endif
Jiri Kosina34c44152006-10-10 14:20:41 -0700828
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200829static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
830
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400831static struct acpi_driver acpi_battery_driver = {
832 .name = "battery",
833 .class = ACPI_BATTERY_CLASS,
834 .ids = battery_device_ids,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600835 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400836 .ops = {
837 .add = acpi_battery_add,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400838 .remove = acpi_battery_remove,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600839 .notify = acpi_battery_notify,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400840 },
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200841 .drv.pm = &acpi_battery_pm,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400842};
843
Linus Torvaldsb0cbc862009-04-11 12:45:20 -0700844static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
Pavel Machek4d8316d2006-08-14 22:37:22 -0700846 if (acpi_disabled)
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500847 return;
Lan Tianyu1e2d9cd2013-10-11 09:54:08 +0800848 acpi_bus_register_driver(&acpi_battery_driver);
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500849}
850
851static int __init acpi_battery_init(void)
852{
853 async_schedule(acpi_battery_init_async, NULL);
Patrick Mocheld550d982006-06-27 00:41:40 -0400854 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855}
856
Len Brown4be44fc2005-08-05 00:44:28 -0400857static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 acpi_bus_unregister_driver(&acpi_battery_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860}
861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862module_init(acpi_battery_init);
863module_exit(acpi_battery_exit);