blob: 018a4288370630cb40203ecd43cc2809851288ae [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
Lan Tianyua90b4032014-01-06 22:50:37 +080064static int battery_bix_broken_package;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040065static unsigned int cache_time = 1000;
66module_param(cache_time, uint, 0644);
67MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030068
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040069static const struct acpi_device_id battery_device_ids[] = {
70 {"PNP0C0A", 0},
71 {"", 0},
72};
73
74MODULE_DEVICE_TABLE(acpi, battery_device_ids);
75
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040076enum {
77 ACPI_BATTERY_ALARM_PRESENT,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +040078 ACPI_BATTERY_XINFO_PRESENT,
Zhang Rui557d5862010-10-22 10:02:06 +080079 ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
Kamil Iskra4000e622012-11-16 22:28:58 +010080 /* On Lenovo Thinkpad models from 2010 and 2011, the power unit
81 switches between mWh and mAh depending on whether the system
82 is running on battery or not. When mAh is the unit, most
83 reported values are incorrect and need to be adjusted by
84 10000/design_voltage. Verified on x201, t410, t410s, and x220.
85 Pre-2010 and 2012 models appear to always report in mWh and
86 are thus unaffected (tested with t42, t61, t500, x200, x300,
87 and x230). Also, in mid-2012 Lenovo issued a BIOS update for
88 the 2011 models that fixes the issue (tested on x220 with a
89 post-1.29 BIOS), but as of Nov. 2012, no such update is
90 available for the 2010 models. */
91 ACPI_BATTERY_QUIRK_THINKPAD_MAH,
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +040092};
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040093
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040094struct acpi_battery {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040095 struct mutex lock;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +030096 struct mutex sysfs_lock;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +040097 struct power_supply bat;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +040098 struct acpi_device *device;
Kyle McMartin25be5822011-03-22 16:19:50 -040099 struct notifier_block pm_nb;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400100 unsigned long update_time;
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200101 int revision;
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400102 int rate_now;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400103 int capacity_now;
104 int voltage_now;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400105 int design_capacity;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400106 int full_charge_capacity;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400107 int technology;
108 int design_voltage;
109 int design_capacity_warning;
110 int design_capacity_low;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400111 int cycle_count;
112 int measurement_accuracy;
113 int max_sampling_time;
114 int min_sampling_time;
115 int max_averaging_interval;
116 int min_averaging_interval;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400117 int capacity_granularity_1;
118 int capacity_granularity_2;
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400119 int alarm;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400120 char model_number[32];
121 char serial_number[32];
122 char type[32];
123 char oem_info[32];
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400124 int state;
125 int power_unit;
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400126 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127};
128
Phil Carmody497888c2011-07-14 15:07:13 +0300129#define to_acpi_battery(x) container_of(x, struct acpi_battery, bat)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400130
Andy Shevchenkoefd941f2013-03-11 09:17:06 +0000131static inline int acpi_battery_present(struct acpi_battery *battery)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400132{
133 return battery->device->status.battery_present;
134}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400135
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400136static int acpi_battery_technology(struct acpi_battery *battery)
137{
138 if (!strcasecmp("NiCd", battery->type))
139 return POWER_SUPPLY_TECHNOLOGY_NiCd;
140 if (!strcasecmp("NiMH", battery->type))
141 return POWER_SUPPLY_TECHNOLOGY_NiMH;
142 if (!strcasecmp("LION", battery->type))
143 return POWER_SUPPLY_TECHNOLOGY_LION;
Andrey Borzenkovad40e682007-11-10 20:02:49 +0300144 if (!strncasecmp("LI-ION", battery->type, 6))
Alexey Starikovskiy0bde7ee2007-10-28 15:33:10 +0300145 return POWER_SUPPLY_TECHNOLOGY_LION;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400146 if (!strcasecmp("LiP", battery->type))
147 return POWER_SUPPLY_TECHNOLOGY_LIPO;
148 return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
149}
150
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300151static int acpi_battery_get_state(struct acpi_battery *battery);
Alexey Starikovskiyb19073a2007-10-25 17:10:47 -0400152
Richard Hughes56f382a2009-01-25 15:05:50 +0000153static int acpi_battery_is_charged(struct acpi_battery *battery)
154{
155 /* either charging or discharging */
156 if (battery->state != 0)
157 return 0;
158
159 /* battery not reporting charge */
160 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN ||
161 battery->capacity_now == 0)
162 return 0;
163
164 /* good batteries update full_charge as the batteries degrade */
165 if (battery->full_charge_capacity == battery->capacity_now)
166 return 1;
167
168 /* fallback to using design values for broken batteries */
169 if (battery->design_capacity == battery->capacity_now)
170 return 1;
171
172 /* we don't do any sort of metric based on percentages */
173 return 0;
174}
175
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400176static int acpi_battery_get_property(struct power_supply *psy,
177 enum power_supply_property psp,
178 union power_supply_propval *val)
179{
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200180 int ret = 0;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400181 struct acpi_battery *battery = to_acpi_battery(psy);
182
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300183 if (acpi_battery_present(battery)) {
184 /* run battery update only if it is present */
185 acpi_battery_get_state(battery);
186 } else if (psp != POWER_SUPPLY_PROP_PRESENT)
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400187 return -ENODEV;
188 switch (psp) {
189 case POWER_SUPPLY_PROP_STATUS:
190 if (battery->state & 0x01)
191 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
192 else if (battery->state & 0x02)
193 val->intval = POWER_SUPPLY_STATUS_CHARGING;
Richard Hughes56f382a2009-01-25 15:05:50 +0000194 else if (acpi_battery_is_charged(battery))
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400195 val->intval = POWER_SUPPLY_STATUS_FULL;
Roland Dreier4c41d3a2007-11-07 15:09:09 -0800196 else
197 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400198 break;
199 case POWER_SUPPLY_PROP_PRESENT:
200 val->intval = acpi_battery_present(battery);
201 break;
202 case POWER_SUPPLY_PROP_TECHNOLOGY:
203 val->intval = acpi_battery_technology(battery);
204 break;
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400205 case POWER_SUPPLY_PROP_CYCLE_COUNT:
206 val->intval = battery->cycle_count;
207 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400208 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200209 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
210 ret = -ENODEV;
211 else
212 val->intval = battery->design_voltage * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400213 break;
214 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200215 if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
216 ret = -ENODEV;
217 else
218 val->intval = battery->voltage_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400219 break;
220 case POWER_SUPPLY_PROP_CURRENT_NOW:
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400221 case POWER_SUPPLY_PROP_POWER_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200222 if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
223 ret = -ENODEV;
224 else
225 val->intval = battery->rate_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400226 break;
227 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
228 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200229 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
230 ret = -ENODEV;
231 else
232 val->intval = battery->design_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400233 break;
234 case POWER_SUPPLY_PROP_CHARGE_FULL:
235 case POWER_SUPPLY_PROP_ENERGY_FULL:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200236 if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
237 ret = -ENODEV;
238 else
239 val->intval = battery->full_charge_capacity * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400240 break;
241 case POWER_SUPPLY_PROP_CHARGE_NOW:
242 case POWER_SUPPLY_PROP_ENERGY_NOW:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200243 if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
244 ret = -ENODEV;
245 else
246 val->intval = battery->capacity_now * 1000;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400247 break;
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700248 case POWER_SUPPLY_PROP_CAPACITY:
249 if (battery->capacity_now && battery->full_charge_capacity)
250 val->intval = battery->capacity_now * 100/
251 battery->full_charge_capacity;
252 else
253 val->intval = 0;
254 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400255 case POWER_SUPPLY_PROP_MODEL_NAME:
256 val->strval = battery->model_number;
257 break;
258 case POWER_SUPPLY_PROP_MANUFACTURER:
259 val->strval = battery->oem_info;
260 break;
maximilian attems7c2670b2008-01-22 18:46:50 +0100261 case POWER_SUPPLY_PROP_SERIAL_NUMBER:
262 val->strval = battery->serial_number;
263 break;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400264 default:
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200265 ret = -EINVAL;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400266 }
Rafael J. Wysockia1b4bd62010-10-23 19:35:15 +0200267 return ret;
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400268}
269
270static enum power_supply_property charge_battery_props[] = {
271 POWER_SUPPLY_PROP_STATUS,
272 POWER_SUPPLY_PROP_PRESENT,
273 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400274 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400275 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
276 POWER_SUPPLY_PROP_VOLTAGE_NOW,
277 POWER_SUPPLY_PROP_CURRENT_NOW,
278 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
279 POWER_SUPPLY_PROP_CHARGE_FULL,
280 POWER_SUPPLY_PROP_CHARGE_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700281 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400282 POWER_SUPPLY_PROP_MODEL_NAME,
283 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100284 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400285};
286
287static enum power_supply_property energy_battery_props[] = {
288 POWER_SUPPLY_PROP_STATUS,
289 POWER_SUPPLY_PROP_PRESENT,
290 POWER_SUPPLY_PROP_TECHNOLOGY,
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400291 POWER_SUPPLY_PROP_CYCLE_COUNT,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400292 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
293 POWER_SUPPLY_PROP_VOLTAGE_NOW,
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400294 POWER_SUPPLY_PROP_POWER_NOW,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400295 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
296 POWER_SUPPLY_PROP_ENERGY_FULL,
297 POWER_SUPPLY_PROP_ENERGY_NOW,
srinivas pandruvadaa58e1152012-04-05 17:38:54 -0700298 POWER_SUPPLY_PROP_CAPACITY,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400299 POWER_SUPPLY_PROP_MODEL_NAME,
300 POWER_SUPPLY_PROP_MANUFACTURER,
maximilian attems7c2670b2008-01-22 18:46:50 +0100301 POWER_SUPPLY_PROP_SERIAL_NUMBER,
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400302};
303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304/* --------------------------------------------------------------------------
305 Battery Management
306 -------------------------------------------------------------------------- */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400307struct acpi_offsets {
308 size_t offset; /* offset inside struct acpi_sbs_battery */
309 u8 mode; /* int or string? */
310};
311
312static struct acpi_offsets state_offsets[] = {
313 {offsetof(struct acpi_battery, state), 0},
Alexey Starikovskiy7faa1442009-03-27 22:23:52 -0400314 {offsetof(struct acpi_battery, rate_now), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400315 {offsetof(struct acpi_battery, capacity_now), 0},
316 {offsetof(struct acpi_battery, voltage_now), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400317};
318
319static struct acpi_offsets info_offsets[] = {
320 {offsetof(struct acpi_battery, power_unit), 0},
321 {offsetof(struct acpi_battery, design_capacity), 0},
Alexey Starikovskiyd7380962007-09-26 19:43:04 +0400322 {offsetof(struct acpi_battery, full_charge_capacity), 0},
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400323 {offsetof(struct acpi_battery, technology), 0},
324 {offsetof(struct acpi_battery, design_voltage), 0},
325 {offsetof(struct acpi_battery, design_capacity_warning), 0},
326 {offsetof(struct acpi_battery, design_capacity_low), 0},
327 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
328 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
329 {offsetof(struct acpi_battery, model_number), 1},
330 {offsetof(struct acpi_battery, serial_number), 1},
331 {offsetof(struct acpi_battery, type), 1},
332 {offsetof(struct acpi_battery, oem_info), 1},
333};
334
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400335static struct acpi_offsets extended_info_offsets[] = {
Lan Tianyu016d5ba2013-07-30 14:00:42 +0200336 {offsetof(struct acpi_battery, revision), 0},
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400337 {offsetof(struct acpi_battery, power_unit), 0},
338 {offsetof(struct acpi_battery, design_capacity), 0},
339 {offsetof(struct acpi_battery, full_charge_capacity), 0},
340 {offsetof(struct acpi_battery, technology), 0},
341 {offsetof(struct acpi_battery, design_voltage), 0},
342 {offsetof(struct acpi_battery, design_capacity_warning), 0},
343 {offsetof(struct acpi_battery, design_capacity_low), 0},
344 {offsetof(struct acpi_battery, cycle_count), 0},
345 {offsetof(struct acpi_battery, measurement_accuracy), 0},
346 {offsetof(struct acpi_battery, max_sampling_time), 0},
347 {offsetof(struct acpi_battery, min_sampling_time), 0},
348 {offsetof(struct acpi_battery, max_averaging_interval), 0},
349 {offsetof(struct acpi_battery, min_averaging_interval), 0},
350 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
351 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
352 {offsetof(struct acpi_battery, model_number), 1},
353 {offsetof(struct acpi_battery, serial_number), 1},
354 {offsetof(struct acpi_battery, type), 1},
355 {offsetof(struct acpi_battery, oem_info), 1},
356};
357
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400358static int extract_package(struct acpi_battery *battery,
359 union acpi_object *package,
360 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300361{
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300362 int i;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400363 union acpi_object *element;
364 if (package->type != ACPI_TYPE_PACKAGE)
365 return -EFAULT;
366 for (i = 0; i < num; ++i) {
367 if (package->package.count <= i)
368 return -EFAULT;
369 element = &package->package.elements[i];
370 if (offsets[i].mode) {
Alexey Starikovskiy106449e2007-10-29 23:29:40 +0300371 u8 *ptr = (u8 *)battery + offsets[i].offset;
372 if (element->type == ACPI_TYPE_STRING ||
373 element->type == ACPI_TYPE_BUFFER)
374 strncpy(ptr, element->string.pointer, 32);
375 else if (element->type == ACPI_TYPE_INTEGER) {
376 strncpy(ptr, (u8 *)&element->integer.value,
Lin Ming439913f2010-01-28 10:53:19 +0800377 sizeof(u64));
378 ptr[sizeof(u64)] = 0;
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400379 } else
380 *ptr = 0; /* don't have value */
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400381 } else {
Alexey Starikovskiyb8a1bdb2008-03-17 22:37:42 -0400382 int *x = (int *)((u8 *)battery + offsets[i].offset);
383 *x = (element->type == ACPI_TYPE_INTEGER) ?
384 element->integer.value : -1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300385 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300386 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300387 return 0;
388}
389
390static int acpi_battery_get_status(struct acpi_battery *battery)
391{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400392 if (acpi_bus_get_status(battery->device)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300393 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
394 return -ENODEV;
395 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400396 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300397}
398
399static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400401 int result = -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400402 acpi_status status = 0;
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000403 char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ?
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400404 "_BIX" : "_BIF";
405
Len Brown4be44fc2005-08-05 00:44:28 -0400406 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300408 if (!acpi_battery_present(battery))
409 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400410 mutex_lock(&battery->lock);
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400411 status = acpi_evaluate_object(battery->device->handle, name,
412 NULL, &buffer);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400413 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 if (ACPI_FAILURE(status)) {
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400416 ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", name));
Patrick Mocheld550d982006-06-27 00:41:40 -0400417 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
Lan Tianyua90b4032014-01-06 22:50:37 +0800419
420 if (battery_bix_broken_package)
421 result = extract_package(battery, buffer.pointer,
422 extended_info_offsets + 1,
423 ARRAY_SIZE(extended_info_offsets) - 1);
424 else if (test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400425 result = extract_package(battery, buffer.pointer,
426 extended_info_offsets,
427 ARRAY_SIZE(extended_info_offsets));
428 else
429 result = extract_package(battery, buffer.pointer,
430 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400431 kfree(buffer.pointer);
Zhang Rui557d5862010-10-22 10:02:06 +0800432 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
433 battery->full_charge_capacity = battery->design_capacity;
Kamil Iskra4000e622012-11-16 22:28:58 +0100434 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
435 battery->power_unit && battery->design_voltage) {
436 battery->design_capacity = battery->design_capacity *
437 10000 / battery->design_voltage;
438 battery->full_charge_capacity = battery->full_charge_capacity *
439 10000 / battery->design_voltage;
440 battery->design_capacity_warning =
441 battery->design_capacity_warning *
442 10000 / battery->design_voltage;
443 /* Curiously, design_capacity_low, unlike the rest of them,
444 is correct. */
445 /* capacity_granularity_* equal 1 on the systems tested, so
446 it's impossible to tell if they would need an adjustment
447 or not if their values were higher. */
448 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400449 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300452static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453{
Len Brown4be44fc2005-08-05 00:44:28 -0400454 int result = 0;
455 acpi_status status = 0;
456 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300458 if (!acpi_battery_present(battery))
459 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400461 if (battery->update_time &&
462 time_before(jiffies, battery->update_time +
463 msecs_to_jiffies(cache_time)))
464 return 0;
465
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400466 mutex_lock(&battery->lock);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400467 status = acpi_evaluate_object(battery->device->handle, "_BST",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400468 NULL, &buffer);
469 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400470
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400472 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400473 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400475
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400476 result = extract_package(battery, buffer.pointer,
477 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400478 battery->update_time = jiffies;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400479 kfree(buffer.pointer);
Hector Martinbc76f902009-08-06 15:57:48 -0700480
Lan Tianyu55003b22011-06-30 11:33:12 +0800481 /* For buggy DSDTs that report negative 16-bit values for either
482 * charging or discharging current and/or report 0 as 65536
483 * due to bad math.
484 */
485 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
486 battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
487 (s16)(battery->rate_now) < 0) {
Hector Martinbc76f902009-08-06 15:57:48 -0700488 battery->rate_now = abs((s16)battery->rate_now);
Lan Tianyu55003b22011-06-30 11:33:12 +0800489 printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
490 " invalid.\n");
491 }
Hector Martinbc76f902009-08-06 15:57:48 -0700492
Zhang Rui557d5862010-10-22 10:02:06 +0800493 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
494 && battery->capacity_now >= 0 && battery->capacity_now <= 100)
495 battery->capacity_now = (battery->capacity_now *
496 battery->full_charge_capacity) / 100;
Kamil Iskra4000e622012-11-16 22:28:58 +0100497 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) &&
498 battery->power_unit && battery->design_voltage) {
499 battery->capacity_now = battery->capacity_now *
500 10000 / battery->design_voltage;
501 }
Patrick Mocheld550d982006-06-27 00:41:40 -0400502 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503}
504
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400505static int acpi_battery_set_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506{
Len Brown4be44fc2005-08-05 00:44:28 -0400507 acpi_status status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400509 if (!acpi_battery_present(battery) ||
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400510 !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300511 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400513 mutex_lock(&battery->lock);
Jiang Liu0db98202013-06-29 00:24:39 +0800514 status = acpi_execute_simple_method(battery->device->handle, "_BTP",
515 battery->alarm);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400516 mutex_unlock(&battery->lock);
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400519 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400521 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm));
Patrick Mocheld550d982006-06-27 00:41:40 -0400522 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523}
524
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300525static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300527 /* See if alarms are supported, and if so, set default */
Jiang Liu952c63e2013-06-29 00:24:38 +0800528 if (!acpi_has_method(battery->device->handle, "_BTP")) {
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400529 clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400530 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 }
Alexey Starikovskiy7b3bcc42009-10-15 14:31:24 +0400532 set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400533 if (!battery->alarm)
534 battery->alarm = battery->design_capacity_warning;
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400535 return acpi_battery_set_alarm(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536}
537
Andrey Borzenkov508df922007-10-28 12:50:09 +0300538static ssize_t acpi_battery_alarm_show(struct device *dev,
539 struct device_attribute *attr,
540 char *buf)
541{
542 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
543 return sprintf(buf, "%d\n", battery->alarm * 1000);
544}
545
546static ssize_t acpi_battery_alarm_store(struct device *dev,
547 struct device_attribute *attr,
548 const char *buf, size_t count)
549{
550 unsigned long x;
551 struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev));
Luis G.F47a08c82014-01-21 15:40:43 +0100552 if (sscanf(buf, "%lu\n", &x) == 1)
Andrey Borzenkov508df922007-10-28 12:50:09 +0300553 battery->alarm = x/1000;
554 if (acpi_battery_present(battery))
555 acpi_battery_set_alarm(battery);
556 return count;
557}
558
559static struct device_attribute alarm_attr = {
Parag Warudkar01e8ef12008-10-18 20:28:50 -0700560 .attr = {.name = "alarm", .mode = 0644},
Andrey Borzenkov508df922007-10-28 12:50:09 +0300561 .show = acpi_battery_alarm_show,
562 .store = acpi_battery_alarm_store,
563};
564
565static int sysfs_add_battery(struct acpi_battery *battery)
566{
567 int result;
568
Lan Tianyuae6f6182011-06-30 11:32:40 +0800569 if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
Andrey Borzenkov508df922007-10-28 12:50:09 +0300570 battery->bat.properties = charge_battery_props;
571 battery->bat.num_properties =
572 ARRAY_SIZE(charge_battery_props);
573 } else {
574 battery->bat.properties = energy_battery_props;
575 battery->bat.num_properties =
576 ARRAY_SIZE(energy_battery_props);
577 }
578
579 battery->bat.name = acpi_device_bid(battery->device);
580 battery->bat.type = POWER_SUPPLY_TYPE_BATTERY;
581 battery->bat.get_property = acpi_battery_get_property;
582
583 result = power_supply_register(&battery->device->dev, &battery->bat);
584 if (result)
585 return result;
586 return device_create_file(battery->bat.dev, &alarm_attr);
587}
588
589static void sysfs_remove_battery(struct acpi_battery *battery)
590{
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300591 mutex_lock(&battery->sysfs_lock);
Lan Tianyu9c921c222011-06-30 11:34:12 +0800592 if (!battery->bat.dev) {
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300593 mutex_unlock(&battery->sysfs_lock);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300594 return;
Lan Tianyu9c921c222011-06-30 11:34:12 +0800595 }
596
Andrey Borzenkov508df922007-10-28 12:50:09 +0300597 device_remove_file(battery->bat.dev, &alarm_attr);
598 power_supply_unregister(&battery->bat);
Alexey Starikovskiy91044762007-11-13 12:23:06 +0300599 battery->bat.dev = NULL;
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300600 mutex_unlock(&battery->sysfs_lock);
Hector Martinbc76f902009-08-06 15:57:48 -0700601}
602
Kamil Iskra4000e622012-11-16 22:28:58 +0100603static void find_battery(const struct dmi_header *dm, void *private)
604{
605 struct acpi_battery *battery = (struct acpi_battery *)private;
606 /* Note: the hardcoded offsets below have been extracted from
607 the source code of dmidecode. */
608 if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) {
609 const u8 *dmi_data = (const u8 *)(dm + 1);
610 int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6));
611 if (dm->length >= 18)
612 dmi_capacity *= dmi_data[17];
613 if (battery->design_capacity * battery->design_voltage / 1000
614 != dmi_capacity &&
615 battery->design_capacity * 10 == dmi_capacity)
616 set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
617 &battery->flags);
618 }
619}
620
Zhang Rui557d5862010-10-22 10:02:06 +0800621/*
622 * According to the ACPI spec, some kinds of primary batteries can
623 * report percentage battery remaining capacity directly to OS.
624 * In this case, it reports the Last Full Charged Capacity == 100
625 * and BatteryPresentRate == 0xFFFFFFFF.
626 *
627 * Now we found some battery reports percentage remaining capacity
628 * even if it's rechargeable.
629 * https://bugzilla.kernel.org/show_bug.cgi?id=15979
630 *
631 * Handle this correctly so that they won't break userspace.
632 */
Lan Tianyu7b786222011-06-30 11:33:27 +0800633static void acpi_battery_quirks(struct acpi_battery *battery)
Zhang Rui557d5862010-10-22 10:02:06 +0800634{
635 if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000636 return;
Zhang Rui557d5862010-10-22 10:02:06 +0800637
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000638 if (battery->full_charge_capacity == 100 &&
639 battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN &&
640 battery->capacity_now >= 0 && battery->capacity_now <= 100) {
Zhang Rui557d5862010-10-22 10:02:06 +0800641 set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags);
642 battery->full_charge_capacity = battery->design_capacity;
643 battery->capacity_now = (battery->capacity_now *
644 battery->full_charge_capacity) / 100;
645 }
Kamil Iskra4000e622012-11-16 22:28:58 +0100646
647 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags))
Nicholas Mazzuca0f4c6542013-05-08 23:11:15 +0000648 return;
Kamil Iskra4000e622012-11-16 22:28:58 +0100649
650 if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
651 const char *s;
652 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
653 if (s && !strnicmp(s, "ThinkPad", 8)) {
654 dmi_walk(find_battery, battery);
655 if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
656 &battery->flags) &&
657 battery->design_voltage) {
658 battery->design_capacity =
659 battery->design_capacity *
660 10000 / battery->design_voltage;
661 battery->full_charge_capacity =
662 battery->full_charge_capacity *
663 10000 / battery->design_voltage;
664 battery->design_capacity_warning =
665 battery->design_capacity_warning *
666 10000 / battery->design_voltage;
667 battery->capacity_now = battery->capacity_now *
668 10000 / battery->design_voltage;
669 }
670 }
671 }
Zhang Rui557d5862010-10-22 10:02:06 +0800672}
673
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400674static int acpi_battery_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500675{
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300676 int result, old_present = acpi_battery_present(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500677 result = acpi_battery_get_status(battery);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300678 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300679 return result;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300680 if (!acpi_battery_present(battery)) {
681 sysfs_remove_battery(battery);
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500682 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300683 return 0;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300684 }
Alexey Starikovskiy50b17852008-12-23 02:44:54 +0300685 if (!battery->update_time ||
686 old_present != acpi_battery_present(battery)) {
Alexey Starikovskiy97749cd2008-01-01 14:27:24 -0500687 result = acpi_battery_get_info(battery);
688 if (result)
689 return result;
690 acpi_battery_init_alarm(battery);
691 }
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100692 if (!battery->bat.dev) {
693 result = sysfs_add_battery(battery);
694 if (result)
695 return result;
696 }
Zhang Rui557d5862010-10-22 10:02:06 +0800697 result = acpi_battery_get_state(battery);
Lan Tianyu7b786222011-06-30 11:33:27 +0800698 acpi_battery_quirks(battery);
Zhang Rui557d5862010-10-22 10:02:06 +0800699 return result;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500700}
701
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100702static void acpi_battery_refresh(struct acpi_battery *battery)
703{
Andy Whitcroftc5971452012-05-03 14:48:26 +0100704 int power_unit;
705
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100706 if (!battery->bat.dev)
707 return;
708
Andy Whitcroftc5971452012-05-03 14:48:26 +0100709 power_unit = battery->power_unit;
710
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100711 acpi_battery_get_info(battery);
Andy Whitcroftc5971452012-05-03 14:48:26 +0100712
713 if (power_unit == battery->power_unit)
714 return;
715
716 /* The battery has changed its reporting units. */
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100717 sysfs_remove_battery(battery);
718 sysfs_add_battery(battery);
719}
720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721/* --------------------------------------------------------------------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 Driver Interface
723 -------------------------------------------------------------------------- */
724
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600725static void acpi_battery_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726{
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600727 struct acpi_battery *battery = acpi_driver_data(device);
Zhang Rui153e5002010-07-07 09:11:57 +0800728 struct device *old;
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600729
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400731 return;
Zhang Rui153e5002010-07-07 09:11:57 +0800732 old = battery->bat.dev;
Rafael J. Wysockida8aeb92011-01-06 23:42:27 +0100733 if (event == ACPI_BATTERY_NOTIFY_INFO)
734 acpi_battery_refresh(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400735 acpi_battery_update(battery);
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400736 acpi_bus_generate_netlink_event(device->pnp.device_class,
Kay Sievers07944692008-10-30 01:18:59 +0100737 dev_name(&device->dev), event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300738 acpi_battery_present(battery));
Justin P. Mattock2345baf2009-12-13 14:42:36 -0800739 /* acpi_battery_update could remove power_supply object */
Zhang Rui153e5002010-07-07 09:11:57 +0800740 if (old && battery->bat.dev)
Alan Jenkinsf79e1ce2009-06-30 14:36:16 +0000741 power_supply_changed(&battery->bat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742}
743
Kyle McMartin25be5822011-03-22 16:19:50 -0400744static int battery_notify(struct notifier_block *nb,
745 unsigned long mode, void *_unused)
746{
747 struct acpi_battery *battery = container_of(nb, struct acpi_battery,
748 pm_nb);
749 switch (mode) {
Lan Tianyud5a59112011-06-30 11:33:40 +0800750 case PM_POST_HIBERNATION:
Kyle McMartin25be5822011-03-22 16:19:50 -0400751 case PM_POST_SUSPEND:
Lan Tianyu6e17fb62011-06-30 11:33:58 +0800752 if (battery->bat.dev) {
753 sysfs_remove_battery(battery);
754 sysfs_add_battery(battery);
755 }
Kyle McMartin25be5822011-03-22 16:19:50 -0400756 break;
757 }
758
759 return 0;
760}
761
Lan Tianyua90b4032014-01-06 22:50:37 +0800762static struct dmi_system_id bat_dmi_table[] = {
763 {
764 .ident = "NEC LZ750/LS",
765 .matches = {
766 DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
767 DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"),
768 },
769 },
770 {},
771};
772
Len Brown4be44fc2005-08-05 00:44:28 -0400773static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Len Brown4be44fc2005-08-05 00:44:28 -0400775 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400776 struct acpi_battery *battery = NULL;
Jiang Liu952c63e2013-06-29 00:24:38 +0800777
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400779 return -EINVAL;
Burman Yan36bcbec2006-12-19 12:56:11 -0800780 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400782 return -ENOMEM;
Patrick Mochel145def82006-05-19 16:54:39 -0400783 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
785 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700786 device->driver_data = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400787 mutex_init(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300788 mutex_init(&battery->sysfs_lock);
Jiang Liu952c63e2013-06-29 00:24:38 +0800789 if (acpi_has_method(battery->device->handle, "_BIX"))
Alexey Starikovskiyc67fcd62009-10-15 14:31:44 +0400790 set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
Stefan Hajnoczieb03cb02011-07-12 09:03:29 +0100791 result = acpi_battery_update(battery);
792 if (result)
793 goto fail;
Kyle McMartin25be5822011-03-22 16:19:50 -0400794
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100795 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
796 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
797 device->status.battery_present ? "present" : "absent");
798
Kyle McMartin25be5822011-03-22 16:19:50 -0400799 battery->pm_nb.notifier_call = battery_notify;
800 register_pm_notifier(&battery->pm_nb);
801
Patrick Mocheld550d982006-06-27 00:41:40 -0400802 return result;
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100803
804fail:
805 sysfs_remove_battery(battery);
806 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300807 mutex_destroy(&battery->sysfs_lock);
Stefan Hajnoczie80bba42011-07-12 09:03:28 +0100808 kfree(battery);
809 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100812static int acpi_battery_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813{
Len Brown4be44fc2005-08-05 00:44:28 -0400814 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400817 return -EINVAL;
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200818 battery = acpi_driver_data(device);
Kyle McMartin25be5822011-03-22 16:19:50 -0400819 unregister_pm_notifier(&battery->pm_nb);
Andrey Borzenkov508df922007-10-28 12:50:09 +0300820 sysfs_remove_battery(battery);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400821 mutex_destroy(&battery->lock);
Sergey Senozhatsky69d94ec2011-08-06 01:34:08 +0300822 mutex_destroy(&battery->sysfs_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 kfree(battery);
Patrick Mocheld550d982006-06-27 00:41:40 -0400824 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200827#ifdef CONFIG_PM_SLEEP
Jiri Kosina34c44152006-10-10 14:20:41 -0700828/* this is needed to learn about changes made in suspended state */
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200829static int acpi_battery_resume(struct device *dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700830{
831 struct acpi_battery *battery;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200832
833 if (!dev)
Jiri Kosina34c44152006-10-10 14:20:41 -0700834 return -EINVAL;
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200835
836 battery = acpi_driver_data(to_acpi_device(dev));
837 if (!battery)
838 return -EINVAL;
839
Alexey Starikovskiyf1d46612007-09-26 19:42:52 +0400840 battery->update_time = 0;
Andrey Borzenkov508df922007-10-28 12:50:09 +0300841 acpi_battery_update(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300842 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700843}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200844#endif
Jiri Kosina34c44152006-10-10 14:20:41 -0700845
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200846static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
847
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400848static struct acpi_driver acpi_battery_driver = {
849 .name = "battery",
850 .class = ACPI_BATTERY_CLASS,
851 .ids = battery_device_ids,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600852 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400853 .ops = {
854 .add = acpi_battery_add,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400855 .remove = acpi_battery_remove,
Bjorn Helgaasd9406692009-04-30 09:35:47 -0600856 .notify = acpi_battery_notify,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400857 },
Rafael J. Wysockia6f50dc2012-06-27 23:26:43 +0200858 .drv.pm = &acpi_battery_pm,
Alexey Starikovskiyaa650bb2007-09-26 19:42:58 +0400859};
860
Linus Torvaldsb0cbc862009-04-11 12:45:20 -0700861static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Pavel Machek4d8316d2006-08-14 22:37:22 -0700863 if (acpi_disabled)
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500864 return;
Lan Tianyua90b4032014-01-06 22:50:37 +0800865
866 if (dmi_check_system(bat_dmi_table))
867 battery_bix_broken_package = 1;
Lan Tianyu1e2d9cd2013-10-11 09:54:08 +0800868 acpi_bus_register_driver(&acpi_battery_driver);
Arjan van de Ven0f66af52009-01-10 14:19:05 -0500869}
870
871static int __init acpi_battery_init(void)
872{
873 async_schedule(acpi_battery_init_async, NULL);
Patrick Mocheld550d982006-06-27 00:41:40 -0400874 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875}
876
Len Brown4be44fc2005-08-05 00:44:28 -0400877static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 acpi_bus_unregister_driver(&acpi_battery_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880}
881
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882module_init(acpi_battery_init);
883module_exit(acpi_battery_exit);