blob: 9a2c63b2005038476e5a5e77360fd836aad3f25f [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 Iskra4000e6262012-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
Alexander Mezinf03be352014-03-12 00:58:46 +070042#include "battery.h"
43
Len Browna192a952009-07-28 16:45:54 -040044#define PREFIX "ACPI: "
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
47
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define ACPI_BATTERY_DEVICE_NAME "Battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Lan Tianyuae6f6182011-06-30 11:32:40 +080050/* Battery power unit: 0 means mW, 1 means mA */
51#define ACPI_BATTERY_POWER_UNIT_MA 1
52
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030054
Len Brownf52fd662007-02-12 22:42:12 -050055ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Len Brownf52fd662007-02-12 22:42:12 -050057MODULE_AUTHOR("Paul Diefenbaugh");
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +040058MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
Len Brown7cda93e2007-02-12 23:50:02 -050059MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070060MODULE_LICENSE("GPL");
61
Lan Tianyua90b4032014-01-06 22:50:37 +080062static int battery_bix_broken_package;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040063static unsigned int cache_time = 1000;
64module_param(cache_time, uint, 0644);
65MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030066
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040067static const struct acpi_device_id battery_device_ids[] = {
68 {"PNP0C0A", 0},
69 {"", 0},
70};
71
72MODULE_DEVICE_TABLE(acpi, battery_device_ids);
73
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040074enum {
75 ACPI_BATTERY_ALARM_PRESENT,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040076 ACPI_BATTERY_XINFO_PRESENT,
Zhang Rui557d5862010-10-22 10:02:06 +080077 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
Kamil Iskra4000e6262012-11-16 22:28:58 +010078 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
79 switches between mWh and mAh depending on whether the system
80 is running on battery or not. When mAh is the unit, most
81 reported values are incorrect and need to be adjusted by
82 10000/design_voltage. Verified on x201, t410, t410s, and x220.
83 Pre-2010 and 2012 models appear to always report in mWh and
84 are thus unaffected (tested with t42, t61, t500, x200, x300,
85 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
86 the 2011 models that fixes the issue (tested on x220 with a
87 post-1.29 BIOS), but as of Nov. 2012, no such update is
88 available for the 2010 models. */
89 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040090};
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040091
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040092struct acpi_battery {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040093 struct mutex lock;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +030094 struct mutex sysfs_lock;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040095 struct power_supply bat;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040096 struct acpi_device *device;
Kyle McMartin25be5822011-03-22 16:19:50 -040097 struct notifier_block pm_nb;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040098 unsigned long update_time;
Lan Tianyu016d5ba2013-07-30 14:00:42 +020099 int revision;
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400100 int rate_now;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400101 int capacity_now;
102 int voltage_now;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400103 int design_capacity;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400104 int full_charge_capacity;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400105 int technology;
106 int design_voltage;
107 int design_capacity_warning;
108 int design_capacity_low;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400109 int cycle_count;
110 int measurement_accuracy;
111 int max_sampling_time;
112 int min_sampling_time;
113 int max_averaging_interval;
114 int min_averaging_interval;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400115 int capacity_granularity_1;
116 int capacity_granularity_2;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400117 int alarm;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400118 char model_number[32];
119 char serial_number[32];
120 char type[32];
121 char oem_info[32];
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400122 int state;
123 int power_unit;
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400124 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
Phil Carmody497888c2011-07-14 15:07:13 +0300127#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400128
Andy Shevchenkoefd941f2013-03-11 09:17:06 +0000129static inline int acpi_battery_present(struct acpi_battery *battery)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400130{
131 return battery->device->status.battery_present;
132}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400133
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400134static int acpi_battery_technology(struct acpi_battery *battery)
135{
136 if (!strcasecmp("NiCd", battery->type))
137 return POWER_SUPPLY_TECHNOLOGY_NiCd;
138 if (!strcasecmp("NiMH", battery->type))
139 return POWER_SUPPLY_TECHNOLOGY_NiMH;
140 if (!strcasecmp("LION", battery->type))
141 return POWER_SUPPLY_TECHNOLOGY_LION;
Andrey Borzenkovad40e682007-11-10 20:02:49 +0300142 if (!strncasecmp("LI-ION", battery->type, 6))
Alexey Starikovskiy0bde7ee2007-10-28 15:33:10 +0300143 return POWER_SUPPLY_TECHNOLOGY_LION;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400144 if (!strcasecmp("LiP", battery->type))
145 return POWER_SUPPLY_TECHNOLOGY_LIPO;
146 return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
147}
148
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300149static int acpi_battery_get_state(struct acpi_battery *battery);
Alexey Starikovskiyb19073a2007-10-25 17:10:47 -0400150
Richard Hughes56f382a2009-01-25 15:05:50 +0000151static int acpi_battery_is_charged(struct acpi_battery *battery)
152{
153 /* either charging or discharging */
154 if (battery->state != 0)
155 return 0;
156
157 /* battery not reporting charge */
158 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
159 battery->capacity_now == 0)
160 return 0;
161
162 /* good batteries update full_charge as the batteries degrade */
163 if (battery->full_charge_capacity == battery->capacity_now)
164 return 1;
165
166 /* fallback to using design values for broken batteries */
167 if (battery->design_capacity == battery->capacity_now)
168 return 1;
169
170 /* we don't do any sort of metric based on percentages */
171 return 0;
172}
173
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400174static int acpi_battery_get_property(struct power_supply *psy,
175 enum power_supply_property psp,
176 union power_supply_propval *val)
177{
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200178 int ret = 0;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400179 struct acpi_battery *battery = to_acpi_battery(psy);
180
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300181 if (acpi_battery_present(battery)) {
182 /* run battery update only if it is present */
183 acpi_battery_get_state(battery);
184 } else if (psp != POWER_SUPPLY_PROP_PRESENT)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400185 return -ENODEV;
186 switch (psp) {
187 case POWER_SUPPLY_PROP_STATUS:
188 if (battery->state & 0x01)
189 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
190 else if (battery->state & 0x02)
191 val->intval = POWER_SUPPLY_STATUS_CHARGING;
Richard Hughes56f382a2009-01-25 15:05:50 +0000192 else if (acpi_battery_is_charged(battery))
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400193 val->intval = POWER_SUPPLY_STATUS_FULL;
Roland Dreier4c41d3a2007-11-07 15:09:09 -0800194 else
195 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400196 break;
197 case POWER_SUPPLY_PROP_PRESENT:
198 val->intval = acpi_battery_present(battery);
199 break;
200 case POWER_SUPPLY_PROP_TECHNOLOGY:
201 val->intval = acpi_battery_technology(battery);
202 break;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400203 case POWER_SUPPLY_PROP_CYCLE_COUNT:
204 val->intval = battery->cycle_count;
205 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400206 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200207 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
208 ret = -ENODEV;
209 else
210 val->intval = battery->design_voltage * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400211 break;
212 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200213 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
214 ret = -ENODEV;
215 else
216 val->intval = battery->voltage_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400217 break;
218 case POWER_SUPPLY_PROP_CURRENT_NOW:
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400219 case POWER_SUPPLY_PROP_POWER_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200220 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
221 ret = -ENODEV;
222 else
223 val->intval = battery->rate_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400224 break;
225 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
226 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200227 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
228 ret = -ENODEV;
229 else
230 val->intval = battery->design_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400231 break;
232 case POWER_SUPPLY_PROP_CHARGE_FULL:
233 case POWER_SUPPLY_PROP_ENERGY_FULL:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200234 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
235 ret = -ENODEV;
236 else
237 val->intval = battery->full_charge_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400238 break;
239 case POWER_SUPPLY_PROP_CHARGE_NOW:
240 case POWER_SUPPLY_PROP_ENERGY_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200241 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
242 ret = -ENODEV;
243 else
244 val->intval = battery->capacity_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400245 break;
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700246 case POWER_SUPPLY_PROP_CAPACITY:
247 if (battery->capacity_now && battery->full_charge_capacity)
248 val->intval = battery->capacity_now * 100/
249 battery->full_charge_capacity;
250 else
251 val->intval = 0;
252 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400253 case POWER_SUPPLY_PROP_MODEL_NAME:
254 val->strval = battery->model_number;
255 break;
256 case POWER_SUPPLY_PROP_MANUFACTURER:
257 val->strval = battery->oem_info;
258 break;
maximilian attems7c2670b2008-01-22 18:46:50 +0100259 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
260 val->strval = battery->serial_number;
261 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400262 default:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200263 ret = -EINVAL;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400264 }
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200265 return ret;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400266}
267
268static enum power_supply_property charge_battery_props[] = {
269 POWER_SUPPLY_PROP_STATUS,
270 POWER_SUPPLY_PROP_PRESENT,
271 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400272 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400273 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
274 POWER_SUPPLY_PROP_VOLTAGE_NOW,
275 POWER_SUPPLY_PROP_CURRENT_NOW,
276 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
277 POWER_SUPPLY_PROP_CHARGE_FULL,
278 POWER_SUPPLY_PROP_CHARGE_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700279 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400280 POWER_SUPPLY_PROP_MODEL_NAME,
281 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100282 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400283};
284
285static enum power_supply_property energy_battery_props[] = {
286 POWER_SUPPLY_PROP_STATUS,
287 POWER_SUPPLY_PROP_PRESENT,
288 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400289 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400290 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
291 POWER_SUPPLY_PROP_VOLTAGE_NOW,
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400292 POWER_SUPPLY_PROP_POWER_NOW,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400293 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
294 POWER_SUPPLY_PROP_ENERGY_FULL,
295 POWER_SUPPLY_PROP_ENERGY_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700296 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400297 POWER_SUPPLY_PROP_MODEL_NAME,
298 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100299 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400300};
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302/* --------------------------------------------------------------------------
303 Battery Management
304 -------------------------------------------------------------------------- */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400305struct acpi_offsets {
306 size_t offset; /* offset inside struct acpi_sbs_battery */
307 u8 mode; /* int or string? */
308};
309
310static struct acpi_offsets state_offsets[] = {
311 {offsetof(struct acpi_battery, state), 0},
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400312 {offsetof(struct acpi_battery, rate_now), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400313 {offsetof(struct acpi_battery, capacity_now), 0},
314 {offsetof(struct acpi_battery, voltage_now), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400315};
316
317static struct acpi_offsets info_offsets[] = {
318 {offsetof(struct acpi_battery, power_unit), 0},
319 {offsetof(struct acpi_battery, design_capacity), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400320 {offsetof(struct acpi_battery, full_charge_capacity), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400321 {offsetof(struct acpi_battery, technology), 0},
322 {offsetof(struct acpi_battery, design_voltage), 0},
323 {offsetof(struct acpi_battery, design_capacity_warning), 0},
324 {offsetof(struct acpi_battery, design_capacity_low), 0},
325 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
326 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
327 {offsetof(struct acpi_battery, model_number), 1},
328 {offsetof(struct acpi_battery, serial_number), 1},
329 {offsetof(struct acpi_battery, type), 1},
330 {offsetof(struct acpi_battery, oem_info), 1},
331};
332
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400333static struct acpi_offsets extended_info_offsets[] = {
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200334 {offsetof(struct acpi_battery, revision), 0},
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400335 {offsetof(struct acpi_battery, power_unit), 0},
336 {offsetof(struct acpi_battery, design_capacity), 0},
337 {offsetof(struct acpi_battery, full_charge_capacity), 0},
338 {offsetof(struct acpi_battery, technology), 0},
339 {offsetof(struct acpi_battery, design_voltage), 0},
340 {offsetof(struct acpi_battery, design_capacity_warning), 0},
341 {offsetof(struct acpi_battery, design_capacity_low), 0},
342 {offsetof(struct acpi_battery, cycle_count), 0},
343 {offsetof(struct acpi_battery, measurement_accuracy), 0},
344 {offsetof(struct acpi_battery, max_sampling_time), 0},
345 {offsetof(struct acpi_battery, min_sampling_time), 0},
346 {offsetof(struct acpi_battery, max_averaging_interval), 0},
347 {offsetof(struct acpi_battery, min_averaging_interval), 0},
348 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
349 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
350 {offsetof(struct acpi_battery, model_number), 1},
351 {offsetof(struct acpi_battery, serial_number), 1},
352 {offsetof(struct acpi_battery, type), 1},
353 {offsetof(struct acpi_battery, oem_info), 1},
354};
355
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400356static int extract_package(struct acpi_battery *battery,
357 union acpi_object *package,
358 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300359{
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300360 int i;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400361 union acpi_object *element;
362 if (package->type != ACPI_TYPE_PACKAGE)
363 return -EFAULT;
364 for (i = 0; i < num; ++i) {
365 if (package->package.count <= i)
366 return -EFAULT;
367 element = &package->package.elements[i];
368 if (offsets[i].mode) {
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300369 u8 *ptr = (u8 *)battery + offsets[i].offset;
370 if (element->type == ACPI_TYPE_STRING ||
371 element->type == ACPI_TYPE_BUFFER)
372 strncpy(ptr, element->string.pointer, 32);
373 else if (element->type == ACPI_TYPE_INTEGER) {
374 strncpy(ptr, (u8 *)&element->integer.value,
Lin Ming439913f2010-01-28 10:53:19 +0800375 sizeof(u64));
376 ptr[sizeof(u64)] = 0;
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400377 } else
378 *ptr = 0; /* don't have value */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400379 } else {
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400380 int *x = (int *)((u8 *)battery + offsets[i].offset);
381 *x = (element->type == ACPI_TYPE_INTEGER) ?
382 element->integer.value : -1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300383 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300384 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300385 return 0;
386}
387
388static int acpi_battery_get_status(struct acpi_battery *battery)
389{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400390 if (acpi_bus_get_status(battery->device)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300391 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
392 return -ENODEV;
393 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400394 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300395}
396
397static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400399 int result = -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400400 acpi_status status = 0;
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000401 char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ?
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400402 "_BIX" : "_BIF";
403
Len Brown4be44fc2005-08-05 00:44:28 -0400404 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300406 if (!acpi_battery_present(battery))
407 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400408 mutex_lock(&battery->lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400409 status = acpi_evaluate_object(battery->device->handle, name,
410 NULL, &buffer);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400411 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 if (ACPI_FAILURE(status)) {
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400414 ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
Patrick Mocheld550d982006-06-27 00:41:40 -0400415 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 }
Lan Tianyua90b4032014-01-06 22:50:37 +0800417
418 if (battery_bix_broken_package)
419 result = extract_package(battery, buffer.pointer,
420 extended_info_offsets + 1,
421 ARRAY_SIZE(extended_info_offsets) - 1);
422 else if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400423 result = extract_package(battery, buffer.pointer,
424 extended_info_offsets,
425 ARRAY_SIZE(extended_info_offsets));
426 else
427 result = extract_package(battery, buffer.pointer,
428 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400429 kfree(buffer.pointer);
Zhang Rui557d5862010-10-22 10:02:06 +0800430 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
431 battery->full_charge_capacity = battery->design_capacity;
Kamil Iskra4000e6262012-11-16 22:28:58 +0100432 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
433 battery->power_unit && battery->design_voltage) {
434 battery->design_capacity = battery->design_capacity *
435 10000 / battery->design_voltage;
436 battery->full_charge_capacity = battery->full_charge_capacity *
437 10000 / battery->design_voltage;
438 battery->design_capacity_warning =
439 battery->design_capacity_warning *
440 10000 / battery->design_voltage;
441 /* Curiously, design_capacity_low, unlike the rest of them,
442 is correct. */
443 /* capacity_granularity_* equal 1 on the systems tested, so
444 it's impossible to tell if they would need an adjustment
445 or not if their values were higher. */
446 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400447 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300450static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Len Brown4be44fc2005-08-05 00:44:28 -0400452 int result = 0;
453 acpi_status status = 0;
454 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300456 if (!acpi_battery_present(battery))
457 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400459 if (battery->update_time &&
460 time_before(jiffies, battery->update_time +
461 msecs_to_jiffies(cache_time)))
462 return 0;
463
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400464 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400465 status = acpi_evaluate_object(battery->device->handle, "_BST",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400466 NULL, &buffer);
467 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400470 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400471 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400473
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400474 result = extract_package(battery, buffer.pointer,
475 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400476 battery->update_time = jiffies;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400477 kfree(buffer.pointer);
Hector Martinbc76f902009-08-06 15:57:48 -0700478
Lan Tianyu55003b22011-06-30 11:33:12 +0800479 /* For buggy DSDTs that report negative 16-bit values for either
480 * charging or discharging current and/or report 0 as 65536
481 * due to bad math.
482 */
483 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
484 battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
485 (s16)(battery->rate_now) < 0) {
Hector Martinbc76f902009-08-06 15:57:48 -0700486 battery->rate_now = abs((s16)battery->rate_now);
Lan Tianyu55003b22011-06-30 11:33:12 +0800487 printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
488 " invalid.\n");
489 }
Hector Martinbc76f902009-08-06 15:57:48 -0700490
Zhang Rui557d5862010-10-22 10:02:06 +0800491 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
492 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
493 battery->capacity_now = (battery->capacity_now *
494 battery->full_charge_capacity) / 100;
Kamil Iskra4000e6262012-11-16 22:28:58 +0100495 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
496 battery->power_unit && battery->design_voltage) {
497 battery->capacity_now = battery->capacity_now *
498 10000 / battery->design_voltage;
499 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400500 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400503static int acpi_battery_set_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504{
Len Brown4be44fc2005-08-05 00:44:28 -0400505 acpi_status status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400507 if (!acpi_battery_present(battery) ||
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400508 !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300509 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400511 mutex_lock(&battery->lock);
Jiang Liu0db98202013-06-29 00:24:39 +0800512 status = acpi_execute_simple_method(battery->device->handle, "_BTP",
513 battery->alarm);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400514 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400517 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400519 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm));
Patrick Mocheld550d982006-06-27 00:41:40 -0400520 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521}
522
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300523static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300525 /* See if alarms are supported, and if so, set default */
Jiang Liu952c63e2013-06-29 00:24:38 +0800526 if (!acpi_has_method(battery->device->handle, "_BTP")) {
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400527 clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400528 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 }
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400530 set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400531 if (!battery->alarm)
532 battery->alarm = battery->design_capacity_warning;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400533 return acpi_battery_set_alarm(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534}
535
Andrey Borzenkov508df922007-10-28 12:50:09 +0300536static ssize_t acpi_battery_alarm_show(struct device *dev,
537 struct device_attribute *attr,
538 char *buf)
539{
540 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
541 return sprintf(buf, "%d\n", battery->alarm * 1000);
542}
543
544static ssize_t acpi_battery_alarm_store(struct device *dev,
545 struct device_attribute *attr,
546 const char *buf, size_t count)
547{
548 unsigned long x;
549 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
Luis G.F47a08c82014-01-21 15:40:43 +0100550 if (sscanf(buf, "%lu\n", &x) == 1)
Andrey Borzenkov508df922007-10-28 12:50:09 +0300551 battery->alarm = x/1000;
552 if (acpi_battery_present(battery))
553 acpi_battery_set_alarm(battery);
554 return count;
555}
556
557static struct device_attribute alarm_attr = {
Parag Warudkar01e8ef12008-10-18 20:28:50 -0700558 .attr = {.name = "alarm", .mode = 0644},
Andrey Borzenkov508df922007-10-28 12:50:09 +0300559 .show = acpi_battery_alarm_show,
560 .store = acpi_battery_alarm_store,
561};
562
563static int sysfs_add_battery(struct acpi_battery *battery)
564{
565 int result;
566
Lan Tianyuae6f6182011-06-30 11:32:40 +0800567 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
Andrey Borzenkov508df922007-10-28 12:50:09 +0300568 battery->bat.properties = charge_battery_props;
569 battery->bat.num_properties =
570 ARRAY_SIZE(charge_battery_props);
571 } else {
572 battery->bat.properties = energy_battery_props;
573 battery->bat.num_properties =
574 ARRAY_SIZE(energy_battery_props);
575 }
576
577 battery->bat.name = acpi_device_bid(battery->device);
578 battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
579 battery->bat.get_property = acpi_battery_get_property;
580
581 result = power_supply_register(&battery->device->dev, &battery->bat);
582 if (result)
583 return result;
584 return device_create_file(battery->bat.dev, &alarm_attr);
585}
586
587static void sysfs_remove_battery(struct acpi_battery *battery)
588{
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300589 mutex_lock(&battery->sysfs_lock);
Lan Tianyu9c921c222011-06-30 11:34:12 +0800590 if (!battery->bat.dev) {
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300591 mutex_unlock(&battery->sysfs_lock);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300592 return;
Lan Tianyu9c921c222011-06-30 11:34:12 +0800593 }
594
Andrey Borzenkov508df922007-10-28 12:50:09 +0300595 device_remove_file(battery->bat.dev, &alarm_attr);
596 power_supply_unregister(&battery->bat);
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300597 battery->bat.dev = NULL;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300598 mutex_unlock(&battery->sysfs_lock);
Hector Martinbc76f902009-08-06 15:57:48 -0700599}
600
Kamil Iskra4000e6262012-11-16 22:28:58 +0100601static void find_battery(const struct dmi_header *dm, void *private)
602{
603 struct acpi_battery *battery = (struct acpi_battery *)private;
604 /* Note: the hardcoded offsets below have been extracted from
605 the source code of dmidecode. */
606 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
607 const u8 *dmi_data = (const u8 *)(dm + 1);
608 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
609 if (dm->length >= 18)
610 dmi_capacity *= dmi_data[17];
611 if (battery->design_capacity * battery->design_voltage / 1000
612 != dmi_capacity &&
613 battery->design_capacity * 10 == dmi_capacity)
614 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
615 &battery->flags);
616 }
617}
618
Zhang Rui557d5862010-10-22 10:02:06 +0800619/*
620 * According to the ACPI spec, some kinds of primary batteries can
621 * report percentage battery remaining capacity directly to OS.
622 * In this case, it reports the Last Full Charged Capacity == 100
623 * and BatteryPresentRate == 0xFFFFFFFF.
624 *
625 * Now we found some battery reports percentage remaining capacity
626 * even if it's rechargeable.
627 * https://bugzilla.kernel.org/show_bug.cgi?id=15979
628 *
629 * Handle this correctly so that they won't break userspace.
630 */
Lan Tianyu7b786222011-06-30 11:33:27 +0800631static void acpi_battery_quirks(struct acpi_battery *battery)
Zhang Rui557d5862010-10-22 10:02:06 +0800632{
633 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000634 return;
Zhang Rui557d5862010-10-22 10:02:06 +0800635
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000636 if (battery->full_charge_capacity == 100 &&
637 battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
638 battery->capacity_now >= 0 && battery->capacity_now <= 100) {
Zhang Rui557d5862010-10-22 10:02:06 +0800639 set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
640 battery->full_charge_capacity = battery->design_capacity;
641 battery->capacity_now = (battery->capacity_now *
642 battery->full_charge_capacity) / 100;
643 }
Kamil Iskra4000e6262012-11-16 22:28:58 +0100644
645 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000646 return;
Kamil Iskra4000e6262012-11-16 22:28:58 +0100647
648 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
649 const char *s;
650 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
651 if (s && !strnicmp(s, "ThinkPad", 8)) {
652 dmi_walk(find_battery, battery);
653 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
654 &battery->flags) &&
655 battery->design_voltage) {
656 battery->design_capacity =
657 battery->design_capacity *
658 10000 / battery->design_voltage;
659 battery->full_charge_capacity =
660 battery->full_charge_capacity *
661 10000 / battery->design_voltage;
662 battery->design_capacity_warning =
663 battery->design_capacity_warning *
664 10000 / battery->design_voltage;
665 battery->capacity_now = battery->capacity_now *
666 10000 / battery->design_voltage;
667 }
668 }
669 }
Zhang Rui557d5862010-10-22 10:02:06 +0800670}
671
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400672static int acpi_battery_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500673{
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300674 int result, old_present = acpi_battery_present(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500675 result = acpi_battery_get_status(battery);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300676 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300677 return result;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300678 if (!acpi_battery_present(battery)) {
679 sysfs_remove_battery(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500680 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300681 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300682 }
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300683 if (!battery->update_time ||
684 old_present != acpi_battery_present(battery)) {
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500685 result = acpi_battery_get_info(battery);
686 if (result)
687 return result;
688 acpi_battery_init_alarm(battery);
689 }
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100690 if (!battery->bat.dev) {
691 result = sysfs_add_battery(battery);
692 if (result)
693 return result;
694 }
Zhang Rui557d5862010-10-22 10:02:06 +0800695 result = acpi_battery_get_state(battery);
Lan Tianyu7b786222011-06-30 11:33:27 +0800696 acpi_battery_quirks(battery);
Zhang Rui557d5862010-10-22 10:02:06 +0800697 return result;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500698}
699
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100700static void acpi_battery_refresh(struct acpi_battery *battery)
701{
Andy Whitcroftc5971452012-05-03 14:48:26 +0100702 int power_unit;
703
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100704 if (!battery->bat.dev)
705 return;
706
Andy Whitcroftc5971452012-05-03 14:48:26 +0100707 power_unit = battery->power_unit;
708
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100709 acpi_battery_get_info(battery);
Andy Whitcroftc5971452012-05-03 14:48:26 +0100710
711 if (power_unit == battery->power_unit)
712 return;
713
714 /* The battery has changed its reporting units. */
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100715 sysfs_remove_battery(battery);
716 sysfs_add_battery(battery);
717}
718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719/* --------------------------------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 Driver Interface
721 -------------------------------------------------------------------------- */
722
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600723static void acpi_battery_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600725 struct acpi_battery *battery = acpi_driver_data(device);
Zhang Rui153e5002010-07-07 09:11:57 +0800726 struct device *old;
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600727
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400729 return;
Zhang Rui153e5002010-07-07 09:11:57 +0800730 old = battery->bat.dev;
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100731 if (event == ACPI_BATTERY_NOTIFY_INFO)
732 acpi_battery_refresh(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400733 acpi_battery_update(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400734 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +0100735 dev_name(&device->dev), event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300736 acpi_battery_present(battery));
Alexander Mezin411e0f72014-03-12 00:58:47 +0700737 acpi_notifier_call_chain(device, event, acpi_battery_present(battery));
Justin P. Mattock2345baf2009-12-13 14:42:36 -0800738 /* acpi_battery_update could remove power_supply object */
Zhang Rui153e5002010-07-07 09:11:57 +0800739 if (old && battery->bat.dev)
Alan Jenkinsf79e1ce2009-06-30 14:36:16 +0000740 power_supply_changed(&battery->bat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741}
742
Kyle McMartin25be5822011-03-22 16:19:50 -0400743static int battery_notify(struct notifier_block *nb,
744 unsigned long mode, void *_unused)
745{
746 struct acpi_battery *battery = container_of(nb, struct acpi_battery,
747 pm_nb);
748 switch (mode) {
Lan Tianyud5a59112011-06-30 11:33:40 +0800749 case PM_POST_HIBERNATION:
Kyle McMartin25be5822011-03-22 16:19:50 -0400750 case PM_POST_SUSPEND:
Lan Tianyu6e17fb62011-06-30 11:33:58 +0800751 if (battery->bat.dev) {
752 sysfs_remove_battery(battery);
753 sysfs_add_battery(battery);
754 }
Kyle McMartin25be5822011-03-22 16:19:50 -0400755 break;
756 }
757
758 return 0;
759}
760
Lan Tianyua90b4032014-01-06 22:50:37 +0800761static struct dmi_system_id bat_dmi_table[] = {
762 {
763 .ident = "NEC LZ750/LS",
764 .matches = {
765 DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
766 DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"),
767 },
768 },
769 {},
770};
771
Len Brown4be44fc2005-08-05 00:44:28 -0400772static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
Len Brown4be44fc2005-08-05 00:44:28 -0400774 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400775 struct acpi_battery *battery = NULL;
Jiang Liu952c63e2013-06-29 00:24:38 +0800776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400778 return -EINVAL;
Burman Yan36bcbec2006-12-19 12:56:11 -0800779 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400781 return -ENOMEM;
Patrick Mochel145def82006-05-19 16:54:39 -0400782 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
784 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700785 device->driver_data = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400786 mutex_init(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300787 mutex_init(&battery->sysfs_lock);
Jiang Liu952c63e2013-06-29 00:24:38 +0800788 if (acpi_has_method(battery->device->handle, "_BIX"))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400789 set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100790 result = acpi_battery_update(battery);
791 if (result)
792 goto fail;
Kyle McMartin25be5822011-03-22 16:19:50 -0400793
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100794 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
795 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
796 device->status.battery_present ? "present" : "absent");
797
Kyle McMartin25be5822011-03-22 16:19:50 -0400798 battery->pm_nb.notifier_call = battery_notify;
799 register_pm_notifier(&battery->pm_nb);
800
Patrick Mocheld550d982006-06-27 00:41:40 -0400801 return result;
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100802
803fail:
804 sysfs_remove_battery(battery);
805 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300806 mutex_destroy(&battery->sysfs_lock);
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100807 kfree(battery);
808 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809}
810
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100811static int acpi_battery_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812{
Len Brown4be44fc2005-08-05 00:44:28 -0400813 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400816 return -EINVAL;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200817 battery = acpi_driver_data(device);
Kyle McMartin25be5822011-03-22 16:19:50 -0400818 unregister_pm_notifier(&battery->pm_nb);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300819 sysfs_remove_battery(battery);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400820 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300821 mutex_destroy(&battery->sysfs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 kfree(battery);
Patrick Mocheld550d982006-06-27 00:41:40 -0400823 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200826#ifdef CONFIG_PM_SLEEP
Jiri Kosina34c44152006-10-10 14:20:41 -0700827/* this is needed to learn about changes made in suspended state */
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200828static int acpi_battery_resume(struct device *dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700829{
830 struct acpi_battery *battery;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200831
832 if (!dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700833 return -EINVAL;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200834
835 battery = acpi_driver_data(to_acpi_device(dev));
836 if (!battery)
837 return -EINVAL;
838
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400839 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300840 acpi_battery_update(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300841 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700842}
Shuah Khan7f6895c2014-02-12 20:19:06 -0700843#else
844#define acpi_battery_resume NULL
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200845#endif
Jiri Kosina34c44152006-10-10 14:20:41 -0700846
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200847static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
848
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400849static struct acpi_driver acpi_battery_driver = {
850 .name = "battery",
851 .class = ACPI_BATTERY_CLASS,
852 .ids = battery_device_ids,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600853 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400854 .ops = {
855 .add = acpi_battery_add,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400856 .remove = acpi_battery_remove,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600857 .notify = acpi_battery_notify,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400858 },
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200859 .drv.pm = &acpi_battery_pm,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400860};
861
Linus Torvaldsb0cbc862009-04-11 12:45:20 -0700862static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863{
Pavel Machek4d8316d2006-08-14 22:37:22 -0700864 if (acpi_disabled)
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500865 return;
Lan Tianyua90b4032014-01-06 22:50:37 +0800866
867 if (dmi_check_system(bat_dmi_table))
868 battery_bix_broken_package = 1;
Lan Tianyu1e2d9cd2013-10-11 09:54:08 +0800869 acpi_bus_register_driver(&acpi_battery_driver);
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500870}
871
872static int __init acpi_battery_init(void)
873{
874 async_schedule(acpi_battery_init_async, NULL);
Patrick Mocheld550d982006-06-27 00:41:40 -0400875 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876}
877
Len Brown4be44fc2005-08-05 00:44:28 -0400878static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 acpi_bus_unregister_driver(&acpi_battery_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881}
882
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883module_init(acpi_battery_init);
884module_exit(acpi_battery_exit);