18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * battery.c - ACPI Battery Driver (Revision: 2.0) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Alexey Starikovskiy <astarikovskiy@suse.de> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004-2007 Vladimir Lebedev <vladimir.p.lebedev@intel.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/async.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/dmi.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/list.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/suspend.h> 238c2ecf20Sopenharmony_ci#include <linux/types.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/acpi.h> 288c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <acpi/battery.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define PREFIX "ACPI: " 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF 358c2ecf20Sopenharmony_ci#define ACPI_BATTERY_CAPACITY_VALID(capacity) \ 368c2ecf20Sopenharmony_ci ((capacity) != 0 && (capacity) != ACPI_BATTERY_VALUE_UNKNOWN) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define ACPI_BATTERY_DEVICE_NAME "Battery" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Battery power unit: 0 means mW, 1 means mA */ 418c2ecf20Sopenharmony_ci#define ACPI_BATTERY_POWER_UNIT_MA 1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_DISCHARGING 0x1 448c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_CHARGING 0x2 458c2ecf20Sopenharmony_ci#define ACPI_BATTERY_STATE_CRITICAL 0x4 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define _COMPONENT ACPI_BATTERY_COMPONENT 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciACPI_MODULE_NAME("battery"); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Diefenbaugh"); 528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>"); 538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI Battery Driver"); 548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic async_cookie_t async_cookie; 578c2ecf20Sopenharmony_cistatic bool battery_driver_registered; 588c2ecf20Sopenharmony_cistatic int battery_bix_broken_package; 598c2ecf20Sopenharmony_cistatic int battery_notification_delay_ms; 608c2ecf20Sopenharmony_cistatic int battery_ac_is_broken; 618c2ecf20Sopenharmony_cistatic int battery_check_pmic = 1; 628c2ecf20Sopenharmony_cistatic int battery_quirk_notcharging; 638c2ecf20Sopenharmony_cistatic unsigned int cache_time = 1000; 648c2ecf20Sopenharmony_cimodule_param(cache_time, uint, 0644); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cache_time, "cache time in milliseconds"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic const struct acpi_device_id battery_device_ids[] = { 688c2ecf20Sopenharmony_ci {"PNP0C0A", 0}, 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Microsoft Surface Go 3 */ 718c2ecf20Sopenharmony_ci {"MSHW0146", 0}, 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci {"", 0}, 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, battery_device_ids); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Lists of PMIC ACPI HIDs with an (often better) native battery driver */ 798c2ecf20Sopenharmony_cistatic const char * const acpi_battery_blacklist[] = { 808c2ecf20Sopenharmony_ci "INT33F4", /* X-Powers AXP288 PMIC */ 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cienum { 848c2ecf20Sopenharmony_ci ACPI_BATTERY_ALARM_PRESENT, 858c2ecf20Sopenharmony_ci ACPI_BATTERY_XINFO_PRESENT, 868c2ecf20Sopenharmony_ci ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, 878c2ecf20Sopenharmony_ci /* On Lenovo Thinkpad models from 2010 and 2011, the power unit 888c2ecf20Sopenharmony_ci switches between mWh and mAh depending on whether the system 898c2ecf20Sopenharmony_ci is running on battery or not. When mAh is the unit, most 908c2ecf20Sopenharmony_ci reported values are incorrect and need to be adjusted by 918c2ecf20Sopenharmony_ci 10000/design_voltage. Verified on x201, t410, t410s, and x220. 928c2ecf20Sopenharmony_ci Pre-2010 and 2012 models appear to always report in mWh and 938c2ecf20Sopenharmony_ci are thus unaffected (tested with t42, t61, t500, x200, x300, 948c2ecf20Sopenharmony_ci and x230). Also, in mid-2012 Lenovo issued a BIOS update for 958c2ecf20Sopenharmony_ci the 2011 models that fixes the issue (tested on x220 with a 968c2ecf20Sopenharmony_ci post-1.29 BIOS), but as of Nov. 2012, no such update is 978c2ecf20Sopenharmony_ci available for the 2010 models. */ 988c2ecf20Sopenharmony_ci ACPI_BATTERY_QUIRK_THINKPAD_MAH, 998c2ecf20Sopenharmony_ci /* for batteries reporting current capacity with design capacity 1008c2ecf20Sopenharmony_ci * on a full charge, but showing degradation in full charge cap. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct acpi_battery { 1068c2ecf20Sopenharmony_ci struct mutex lock; 1078c2ecf20Sopenharmony_ci struct mutex sysfs_lock; 1088c2ecf20Sopenharmony_ci struct power_supply *bat; 1098c2ecf20Sopenharmony_ci struct power_supply_desc bat_desc; 1108c2ecf20Sopenharmony_ci struct acpi_device *device; 1118c2ecf20Sopenharmony_ci struct notifier_block pm_nb; 1128c2ecf20Sopenharmony_ci struct list_head list; 1138c2ecf20Sopenharmony_ci unsigned long update_time; 1148c2ecf20Sopenharmony_ci int revision; 1158c2ecf20Sopenharmony_ci int rate_now; 1168c2ecf20Sopenharmony_ci int capacity_now; 1178c2ecf20Sopenharmony_ci int voltage_now; 1188c2ecf20Sopenharmony_ci int design_capacity; 1198c2ecf20Sopenharmony_ci int full_charge_capacity; 1208c2ecf20Sopenharmony_ci int technology; 1218c2ecf20Sopenharmony_ci int design_voltage; 1228c2ecf20Sopenharmony_ci int design_capacity_warning; 1238c2ecf20Sopenharmony_ci int design_capacity_low; 1248c2ecf20Sopenharmony_ci int cycle_count; 1258c2ecf20Sopenharmony_ci int measurement_accuracy; 1268c2ecf20Sopenharmony_ci int max_sampling_time; 1278c2ecf20Sopenharmony_ci int min_sampling_time; 1288c2ecf20Sopenharmony_ci int max_averaging_interval; 1298c2ecf20Sopenharmony_ci int min_averaging_interval; 1308c2ecf20Sopenharmony_ci int capacity_granularity_1; 1318c2ecf20Sopenharmony_ci int capacity_granularity_2; 1328c2ecf20Sopenharmony_ci int alarm; 1338c2ecf20Sopenharmony_ci char model_number[32]; 1348c2ecf20Sopenharmony_ci char serial_number[32]; 1358c2ecf20Sopenharmony_ci char type[32]; 1368c2ecf20Sopenharmony_ci char oem_info[32]; 1378c2ecf20Sopenharmony_ci int state; 1388c2ecf20Sopenharmony_ci int power_unit; 1398c2ecf20Sopenharmony_ci unsigned long flags; 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define to_acpi_battery(x) power_supply_get_drvdata(x) 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic inline int acpi_battery_present(struct acpi_battery *battery) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci return battery->device->status.battery_present; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int acpi_battery_technology(struct acpi_battery *battery) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci if (!strcasecmp("NiCd", battery->type)) 1528c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_NiCd; 1538c2ecf20Sopenharmony_ci if (!strcasecmp("NiMH", battery->type)) 1548c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_NiMH; 1558c2ecf20Sopenharmony_ci if (!strcasecmp("LION", battery->type)) 1568c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_LION; 1578c2ecf20Sopenharmony_ci if (!strncasecmp("LI-ION", battery->type, 6)) 1588c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_LION; 1598c2ecf20Sopenharmony_ci if (!strcasecmp("LiP", battery->type)) 1608c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_LIPO; 1618c2ecf20Sopenharmony_ci return POWER_SUPPLY_TECHNOLOGY_UNKNOWN; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int acpi_battery_get_state(struct acpi_battery *battery); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int acpi_battery_is_charged(struct acpi_battery *battery) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci /* charging, discharging or critical low */ 1698c2ecf20Sopenharmony_ci if (battery->state != 0) 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* battery not reporting charge */ 1738c2ecf20Sopenharmony_ci if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || 1748c2ecf20Sopenharmony_ci battery->capacity_now == 0) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* good batteries update full_charge as the batteries degrade */ 1788c2ecf20Sopenharmony_ci if (battery->full_charge_capacity == battery->capacity_now) 1798c2ecf20Sopenharmony_ci return 1; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* fallback to using design values for broken batteries */ 1828c2ecf20Sopenharmony_ci if (battery->design_capacity <= battery->capacity_now) 1838c2ecf20Sopenharmony_ci return 1; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* we don't do any sort of metric based on percentages */ 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic bool acpi_battery_is_degraded(struct acpi_battery *battery) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci return ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && 1928c2ecf20Sopenharmony_ci ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity) && 1938c2ecf20Sopenharmony_ci battery->full_charge_capacity < battery->design_capacity; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int acpi_battery_handle_discharging(struct acpi_battery *battery) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Some devices wrongly report discharging if the battery's charge level 2008c2ecf20Sopenharmony_ci * was above the device's start charging threshold atm the AC adapter 2018c2ecf20Sopenharmony_ci * was plugged in and the device thus did not start a new charge cycle. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci if ((battery_ac_is_broken || power_supply_is_system_supplied()) && 2048c2ecf20Sopenharmony_ci battery->rate_now == 0) 2058c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_NOT_CHARGING; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return POWER_SUPPLY_STATUS_DISCHARGING; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int acpi_battery_get_property(struct power_supply *psy, 2118c2ecf20Sopenharmony_ci enum power_supply_property psp, 2128c2ecf20Sopenharmony_ci union power_supply_propval *val) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int full_capacity = ACPI_BATTERY_VALUE_UNKNOWN, ret = 0; 2158c2ecf20Sopenharmony_ci struct acpi_battery *battery = to_acpi_battery(psy); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (acpi_battery_present(battery)) { 2188c2ecf20Sopenharmony_ci /* run battery update only if it is present */ 2198c2ecf20Sopenharmony_ci acpi_battery_get_state(battery); 2208c2ecf20Sopenharmony_ci } else if (psp != POWER_SUPPLY_PROP_PRESENT) 2218c2ecf20Sopenharmony_ci return -ENODEV; 2228c2ecf20Sopenharmony_ci switch (psp) { 2238c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 2248c2ecf20Sopenharmony_ci if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) 2258c2ecf20Sopenharmony_ci val->intval = acpi_battery_handle_discharging(battery); 2268c2ecf20Sopenharmony_ci else if (battery->state & ACPI_BATTERY_STATE_CHARGING) 2278c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 2288c2ecf20Sopenharmony_ci else if (acpi_battery_is_charged(battery)) 2298c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 2308c2ecf20Sopenharmony_ci else if (battery_quirk_notcharging) 2318c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 2328c2ecf20Sopenharmony_ci else 2338c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 2368c2ecf20Sopenharmony_ci val->intval = acpi_battery_present(battery); 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 2398c2ecf20Sopenharmony_ci val->intval = acpi_battery_technology(battery); 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CYCLE_COUNT: 2428c2ecf20Sopenharmony_ci val->intval = battery->cycle_count; 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 2458c2ecf20Sopenharmony_ci if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) 2468c2ecf20Sopenharmony_ci ret = -ENODEV; 2478c2ecf20Sopenharmony_ci else 2488c2ecf20Sopenharmony_ci val->intval = battery->design_voltage * 1000; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 2518c2ecf20Sopenharmony_ci if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) 2528c2ecf20Sopenharmony_ci ret = -ENODEV; 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci val->intval = battery->voltage_now * 1000; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 2578c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_POWER_NOW: 2588c2ecf20Sopenharmony_ci if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN) 2598c2ecf20Sopenharmony_ci ret = -ENODEV; 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci val->intval = battery->rate_now * 1000; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 2648c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 2658c2ecf20Sopenharmony_ci if (!ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) 2668c2ecf20Sopenharmony_ci ret = -ENODEV; 2678c2ecf20Sopenharmony_ci else 2688c2ecf20Sopenharmony_ci val->intval = battery->design_capacity * 1000; 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 2718c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL: 2728c2ecf20Sopenharmony_ci if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity)) 2738c2ecf20Sopenharmony_ci ret = -ENODEV; 2748c2ecf20Sopenharmony_ci else 2758c2ecf20Sopenharmony_ci val->intval = battery->full_charge_capacity * 1000; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 2788c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_NOW: 2798c2ecf20Sopenharmony_ci if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) 2808c2ecf20Sopenharmony_ci ret = -ENODEV; 2818c2ecf20Sopenharmony_ci else 2828c2ecf20Sopenharmony_ci val->intval = battery->capacity_now * 1000; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 2858c2ecf20Sopenharmony_ci if (ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity)) 2868c2ecf20Sopenharmony_ci full_capacity = battery->full_charge_capacity; 2878c2ecf20Sopenharmony_ci else if (ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) 2888c2ecf20Sopenharmony_ci full_capacity = battery->design_capacity; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN || 2918c2ecf20Sopenharmony_ci full_capacity == ACPI_BATTERY_VALUE_UNKNOWN) 2928c2ecf20Sopenharmony_ci ret = -ENODEV; 2938c2ecf20Sopenharmony_ci else 2948c2ecf20Sopenharmony_ci val->intval = battery->capacity_now * 100/ 2958c2ecf20Sopenharmony_ci full_capacity; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 2988c2ecf20Sopenharmony_ci if (battery->state & ACPI_BATTERY_STATE_CRITICAL) 2998c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 3008c2ecf20Sopenharmony_ci else if (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && 3018c2ecf20Sopenharmony_ci (battery->capacity_now <= battery->alarm)) 3028c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 3038c2ecf20Sopenharmony_ci else if (acpi_battery_is_charged(battery)) 3048c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 3058c2ecf20Sopenharmony_ci else 3068c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 3098c2ecf20Sopenharmony_ci val->strval = battery->model_number; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 3128c2ecf20Sopenharmony_ci val->strval = battery->oem_info; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_SERIAL_NUMBER: 3158c2ecf20Sopenharmony_ci val->strval = battery->serial_number; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci ret = -EINVAL; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci return ret; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic enum power_supply_property charge_battery_props[] = { 3248c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3258c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 3268c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 3278c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CYCLE_COUNT, 3288c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 3298c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3308c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 3318c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 3328c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 3338c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 3348c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 3358c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY_LEVEL, 3368c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 3378c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 3388c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_SERIAL_NUMBER, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic enum power_supply_property charge_battery_full_cap_broken_props[] = { 3428c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3438c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 3448c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 3458c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CYCLE_COUNT, 3468c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 3478c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3488c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 3498c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 3508c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 3518c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 3528c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_SERIAL_NUMBER, 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic enum power_supply_property energy_battery_props[] = { 3568c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3578c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 3588c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 3598c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CYCLE_COUNT, 3608c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 3618c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3628c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_POWER_NOW, 3638c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 3648c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL, 3658c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_NOW, 3668c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 3678c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY_LEVEL, 3688c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 3698c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 3708c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_SERIAL_NUMBER, 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic enum power_supply_property energy_battery_full_cap_broken_props[] = { 3748c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 3758c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 3768c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 3778c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CYCLE_COUNT, 3788c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 3798c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 3808c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_POWER_NOW, 3818c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_NOW, 3828c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 3838c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 3848c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_SERIAL_NUMBER, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 3888c2ecf20Sopenharmony_ci Battery Management 3898c2ecf20Sopenharmony_ci -------------------------------------------------------------------------- */ 3908c2ecf20Sopenharmony_cistruct acpi_offsets { 3918c2ecf20Sopenharmony_ci size_t offset; /* offset inside struct acpi_sbs_battery */ 3928c2ecf20Sopenharmony_ci u8 mode; /* int or string? */ 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct acpi_offsets state_offsets[] = { 3968c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, state), 0}, 3978c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, rate_now), 0}, 3988c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, capacity_now), 0}, 3998c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, voltage_now), 0}, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct acpi_offsets info_offsets[] = { 4038c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, power_unit), 0}, 4048c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity), 0}, 4058c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, full_charge_capacity), 0}, 4068c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, technology), 0}, 4078c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_voltage), 0}, 4088c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity_warning), 0}, 4098c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity_low), 0}, 4108c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, capacity_granularity_1), 0}, 4118c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, capacity_granularity_2), 0}, 4128c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, model_number), 1}, 4138c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, serial_number), 1}, 4148c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, type), 1}, 4158c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, oem_info), 1}, 4168c2ecf20Sopenharmony_ci}; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic const struct acpi_offsets extended_info_offsets[] = { 4198c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, revision), 0}, 4208c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, power_unit), 0}, 4218c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity), 0}, 4228c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, full_charge_capacity), 0}, 4238c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, technology), 0}, 4248c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_voltage), 0}, 4258c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity_warning), 0}, 4268c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, design_capacity_low), 0}, 4278c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, cycle_count), 0}, 4288c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, measurement_accuracy), 0}, 4298c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, max_sampling_time), 0}, 4308c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, min_sampling_time), 0}, 4318c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, max_averaging_interval), 0}, 4328c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, min_averaging_interval), 0}, 4338c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, capacity_granularity_1), 0}, 4348c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, capacity_granularity_2), 0}, 4358c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, model_number), 1}, 4368c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, serial_number), 1}, 4378c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, type), 1}, 4388c2ecf20Sopenharmony_ci {offsetof(struct acpi_battery, oem_info), 1}, 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int extract_package(struct acpi_battery *battery, 4428c2ecf20Sopenharmony_ci union acpi_object *package, 4438c2ecf20Sopenharmony_ci const struct acpi_offsets *offsets, int num) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int i; 4468c2ecf20Sopenharmony_ci union acpi_object *element; 4478c2ecf20Sopenharmony_ci if (package->type != ACPI_TYPE_PACKAGE) 4488c2ecf20Sopenharmony_ci return -EFAULT; 4498c2ecf20Sopenharmony_ci for (i = 0; i < num; ++i) { 4508c2ecf20Sopenharmony_ci if (package->package.count <= i) 4518c2ecf20Sopenharmony_ci return -EFAULT; 4528c2ecf20Sopenharmony_ci element = &package->package.elements[i]; 4538c2ecf20Sopenharmony_ci if (offsets[i].mode) { 4548c2ecf20Sopenharmony_ci u8 *ptr = (u8 *)battery + offsets[i].offset; 4558c2ecf20Sopenharmony_ci if (element->type == ACPI_TYPE_STRING || 4568c2ecf20Sopenharmony_ci element->type == ACPI_TYPE_BUFFER) 4578c2ecf20Sopenharmony_ci strscpy(ptr, element->string.pointer, 32); 4588c2ecf20Sopenharmony_ci else if (element->type == ACPI_TYPE_INTEGER) { 4598c2ecf20Sopenharmony_ci strncpy(ptr, (u8 *)&element->integer.value, 4608c2ecf20Sopenharmony_ci sizeof(u64)); 4618c2ecf20Sopenharmony_ci ptr[sizeof(u64)] = 0; 4628c2ecf20Sopenharmony_ci } else 4638c2ecf20Sopenharmony_ci *ptr = 0; /* don't have value */ 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci int *x = (int *)((u8 *)battery + offsets[i].offset); 4668c2ecf20Sopenharmony_ci *x = (element->type == ACPI_TYPE_INTEGER) ? 4678c2ecf20Sopenharmony_ci element->integer.value : -1; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int acpi_battery_get_status(struct acpi_battery *battery) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci if (acpi_bus_get_status(battery->device)) { 4768c2ecf20Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA")); 4778c2ecf20Sopenharmony_ci return -ENODEV; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int extract_battery_info(const int use_bix, 4848c2ecf20Sopenharmony_ci struct acpi_battery *battery, 4858c2ecf20Sopenharmony_ci const struct acpi_buffer *buffer) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int result = -EFAULT; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (use_bix && battery_bix_broken_package) 4908c2ecf20Sopenharmony_ci result = extract_package(battery, buffer->pointer, 4918c2ecf20Sopenharmony_ci extended_info_offsets + 1, 4928c2ecf20Sopenharmony_ci ARRAY_SIZE(extended_info_offsets) - 1); 4938c2ecf20Sopenharmony_ci else if (use_bix) 4948c2ecf20Sopenharmony_ci result = extract_package(battery, buffer->pointer, 4958c2ecf20Sopenharmony_ci extended_info_offsets, 4968c2ecf20Sopenharmony_ci ARRAY_SIZE(extended_info_offsets)); 4978c2ecf20Sopenharmony_ci else 4988c2ecf20Sopenharmony_ci result = extract_package(battery, buffer->pointer, 4998c2ecf20Sopenharmony_ci info_offsets, ARRAY_SIZE(info_offsets)); 5008c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) 5018c2ecf20Sopenharmony_ci battery->full_charge_capacity = battery->design_capacity; 5028c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) && 5038c2ecf20Sopenharmony_ci battery->power_unit && battery->design_voltage) { 5048c2ecf20Sopenharmony_ci battery->design_capacity = battery->design_capacity * 5058c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 5068c2ecf20Sopenharmony_ci battery->full_charge_capacity = battery->full_charge_capacity * 5078c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 5088c2ecf20Sopenharmony_ci battery->design_capacity_warning = 5098c2ecf20Sopenharmony_ci battery->design_capacity_warning * 5108c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 5118c2ecf20Sopenharmony_ci /* Curiously, design_capacity_low, unlike the rest of them, 5128c2ecf20Sopenharmony_ci is correct. */ 5138c2ecf20Sopenharmony_ci /* capacity_granularity_* equal 1 on the systems tested, so 5148c2ecf20Sopenharmony_ci it's impossible to tell if they would need an adjustment 5158c2ecf20Sopenharmony_ci or not if their values were higher. */ 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) && 5188c2ecf20Sopenharmony_ci battery->capacity_now > battery->full_charge_capacity) 5198c2ecf20Sopenharmony_ci battery->capacity_now = battery->full_charge_capacity; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return result; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic int acpi_battery_get_info(struct acpi_battery *battery) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci const int xinfo = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); 5278c2ecf20Sopenharmony_ci int use_bix; 5288c2ecf20Sopenharmony_ci int result = -ENODEV; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!acpi_battery_present(battery)) 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci for (use_bix = xinfo ? 1 : 0; use_bix >= 0; use_bix--) { 5358c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 5368c2ecf20Sopenharmony_ci acpi_status status = AE_ERROR; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&battery->lock); 5398c2ecf20Sopenharmony_ci status = acpi_evaluate_object(battery->device->handle, 5408c2ecf20Sopenharmony_ci use_bix ? "_BIX":"_BIF", 5418c2ecf20Sopenharmony_ci NULL, &buffer); 5428c2ecf20Sopenharmony_ci mutex_unlock(&battery->lock); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5458c2ecf20Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, "Evaluating %s", 5468c2ecf20Sopenharmony_ci use_bix ? "_BIX":"_BIF")); 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci result = extract_battery_info(use_bix, 5498c2ecf20Sopenharmony_ci battery, 5508c2ecf20Sopenharmony_ci &buffer); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci kfree(buffer.pointer); 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (!result && !use_bix && xinfo) 5588c2ecf20Sopenharmony_ci pr_warn(FW_BUG "The _BIX method is broken, using _BIF.\n"); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return result; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int acpi_battery_get_state(struct acpi_battery *battery) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci int result = 0; 5668c2ecf20Sopenharmony_ci acpi_status status = 0; 5678c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!acpi_battery_present(battery)) 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (battery->update_time && 5738c2ecf20Sopenharmony_ci time_before(jiffies, battery->update_time + 5748c2ecf20Sopenharmony_ci msecs_to_jiffies(cache_time))) 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci mutex_lock(&battery->lock); 5788c2ecf20Sopenharmony_ci status = acpi_evaluate_object(battery->device->handle, "_BST", 5798c2ecf20Sopenharmony_ci NULL, &buffer); 5808c2ecf20Sopenharmony_ci mutex_unlock(&battery->lock); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5838c2ecf20Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST")); 5848c2ecf20Sopenharmony_ci return -ENODEV; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci result = extract_package(battery, buffer.pointer, 5888c2ecf20Sopenharmony_ci state_offsets, ARRAY_SIZE(state_offsets)); 5898c2ecf20Sopenharmony_ci battery->update_time = jiffies; 5908c2ecf20Sopenharmony_ci kfree(buffer.pointer); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* For buggy DSDTs that report negative 16-bit values for either 5938c2ecf20Sopenharmony_ci * charging or discharging current and/or report 0 as 65536 5948c2ecf20Sopenharmony_ci * due to bad math. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA && 5978c2ecf20Sopenharmony_ci battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && 5988c2ecf20Sopenharmony_ci (s16)(battery->rate_now) < 0) { 5998c2ecf20Sopenharmony_ci battery->rate_now = abs((s16)battery->rate_now); 6008c2ecf20Sopenharmony_ci pr_warn_once(FW_BUG "battery: (dis)charge rate invalid.\n"); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags) 6048c2ecf20Sopenharmony_ci && battery->capacity_now >= 0 && battery->capacity_now <= 100) 6058c2ecf20Sopenharmony_ci battery->capacity_now = (battery->capacity_now * 6068c2ecf20Sopenharmony_ci battery->full_charge_capacity) / 100; 6078c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags) && 6088c2ecf20Sopenharmony_ci battery->power_unit && battery->design_voltage) { 6098c2ecf20Sopenharmony_ci battery->capacity_now = battery->capacity_now * 6108c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags) && 6138c2ecf20Sopenharmony_ci battery->capacity_now > battery->full_charge_capacity) 6148c2ecf20Sopenharmony_ci battery->capacity_now = battery->full_charge_capacity; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return result; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int acpi_battery_set_alarm(struct acpi_battery *battery) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci acpi_status status = 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (!acpi_battery_present(battery) || 6248c2ecf20Sopenharmony_ci !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) 6258c2ecf20Sopenharmony_ci return -ENODEV; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci mutex_lock(&battery->lock); 6288c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(battery->device->handle, "_BTP", 6298c2ecf20Sopenharmony_ci battery->alarm); 6308c2ecf20Sopenharmony_ci mutex_unlock(&battery->lock); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6338c2ecf20Sopenharmony_ci return -ENODEV; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", battery->alarm)); 6368c2ecf20Sopenharmony_ci return 0; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int acpi_battery_init_alarm(struct acpi_battery *battery) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci /* See if alarms are supported, and if so, set default */ 6428c2ecf20Sopenharmony_ci if (!acpi_has_method(battery->device->handle, "_BTP")) { 6438c2ecf20Sopenharmony_ci clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci set_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); 6478c2ecf20Sopenharmony_ci if (!battery->alarm) 6488c2ecf20Sopenharmony_ci battery->alarm = battery->design_capacity_warning; 6498c2ecf20Sopenharmony_ci return acpi_battery_set_alarm(battery); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic ssize_t acpi_battery_alarm_show(struct device *dev, 6538c2ecf20Sopenharmony_ci struct device_attribute *attr, 6548c2ecf20Sopenharmony_ci char *buf) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); 6578c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", battery->alarm * 1000); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic ssize_t acpi_battery_alarm_store(struct device *dev, 6618c2ecf20Sopenharmony_ci struct device_attribute *attr, 6628c2ecf20Sopenharmony_ci const char *buf, size_t count) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci unsigned long x; 6658c2ecf20Sopenharmony_ci struct acpi_battery *battery = to_acpi_battery(dev_get_drvdata(dev)); 6668c2ecf20Sopenharmony_ci if (sscanf(buf, "%lu\n", &x) == 1) 6678c2ecf20Sopenharmony_ci battery->alarm = x/1000; 6688c2ecf20Sopenharmony_ci if (acpi_battery_present(battery)) 6698c2ecf20Sopenharmony_ci acpi_battery_set_alarm(battery); 6708c2ecf20Sopenharmony_ci return count; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic const struct device_attribute alarm_attr = { 6748c2ecf20Sopenharmony_ci .attr = {.name = "alarm", .mode = 0644}, 6758c2ecf20Sopenharmony_ci .show = acpi_battery_alarm_show, 6768c2ecf20Sopenharmony_ci .store = acpi_battery_alarm_store, 6778c2ecf20Sopenharmony_ci}; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci/* 6808c2ecf20Sopenharmony_ci * The Battery Hooking API 6818c2ecf20Sopenharmony_ci * 6828c2ecf20Sopenharmony_ci * This API is used inside other drivers that need to expose 6838c2ecf20Sopenharmony_ci * platform-specific behaviour within the generic driver in a 6848c2ecf20Sopenharmony_ci * generic way. 6858c2ecf20Sopenharmony_ci * 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic LIST_HEAD(acpi_battery_list); 6898c2ecf20Sopenharmony_cistatic LIST_HEAD(battery_hook_list); 6908c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(hook_mutex); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct acpi_battery *battery; 6958c2ecf20Sopenharmony_ci /* 6968c2ecf20Sopenharmony_ci * In order to remove a hook, we first need to 6978c2ecf20Sopenharmony_ci * de-register all the batteries that are registered. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci if (lock) 7008c2ecf20Sopenharmony_ci mutex_lock(&hook_mutex); 7018c2ecf20Sopenharmony_ci list_for_each_entry(battery, &acpi_battery_list, list) { 7028c2ecf20Sopenharmony_ci hook->remove_battery(battery->bat); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci list_del(&hook->list); 7058c2ecf20Sopenharmony_ci if (lock) 7068c2ecf20Sopenharmony_ci mutex_unlock(&hook_mutex); 7078c2ecf20Sopenharmony_ci pr_info("extension unregistered: %s\n", hook->name); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_civoid battery_hook_unregister(struct acpi_battery_hook *hook) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci __battery_hook_unregister(hook, 1); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(battery_hook_unregister); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_civoid battery_hook_register(struct acpi_battery_hook *hook) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct acpi_battery *battery; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci mutex_lock(&hook_mutex); 7218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hook->list); 7228c2ecf20Sopenharmony_ci list_add(&hook->list, &battery_hook_list); 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * Now that the driver is registered, we need 7258c2ecf20Sopenharmony_ci * to notify the hook that a battery is available 7268c2ecf20Sopenharmony_ci * for each battery, so that the driver may add 7278c2ecf20Sopenharmony_ci * its attributes. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci list_for_each_entry(battery, &acpi_battery_list, list) { 7308c2ecf20Sopenharmony_ci if (hook->add_battery(battery->bat)) { 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * If a add-battery returns non-zero, 7338c2ecf20Sopenharmony_ci * the registration of the extension has failed, 7348c2ecf20Sopenharmony_ci * and we will not add it to the list of loaded 7358c2ecf20Sopenharmony_ci * hooks. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci pr_err("extension failed to load: %s", hook->name); 7388c2ecf20Sopenharmony_ci __battery_hook_unregister(hook, 0); 7398c2ecf20Sopenharmony_ci goto end; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci pr_info("new extension: %s\n", hook->name); 7438c2ecf20Sopenharmony_ciend: 7448c2ecf20Sopenharmony_ci mutex_unlock(&hook_mutex); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(battery_hook_register); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* 7498c2ecf20Sopenharmony_ci * This function gets called right after the battery sysfs 7508c2ecf20Sopenharmony_ci * attributes have been added, so that the drivers that 7518c2ecf20Sopenharmony_ci * define custom sysfs attributes can add their own. 7528c2ecf20Sopenharmony_ci*/ 7538c2ecf20Sopenharmony_cistatic void battery_hook_add_battery(struct acpi_battery *battery) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct acpi_battery_hook *hook_node, *tmp; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci mutex_lock(&hook_mutex); 7588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&battery->list); 7598c2ecf20Sopenharmony_ci list_add(&battery->list, &acpi_battery_list); 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * Since we added a new battery to the list, we need to 7628c2ecf20Sopenharmony_ci * iterate over the hooks and call add_battery for each 7638c2ecf20Sopenharmony_ci * hook that was registered. This usually happens 7648c2ecf20Sopenharmony_ci * when a battery gets hotplugged or initialized 7658c2ecf20Sopenharmony_ci * during the battery module initialization. 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_ci list_for_each_entry_safe(hook_node, tmp, &battery_hook_list, list) { 7688c2ecf20Sopenharmony_ci if (hook_node->add_battery(battery->bat)) { 7698c2ecf20Sopenharmony_ci /* 7708c2ecf20Sopenharmony_ci * The notification of the extensions has failed, to 7718c2ecf20Sopenharmony_ci * prevent further errors we will unload the extension. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_ci pr_err("error in extension, unloading: %s", 7748c2ecf20Sopenharmony_ci hook_node->name); 7758c2ecf20Sopenharmony_ci __battery_hook_unregister(hook_node, 0); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci mutex_unlock(&hook_mutex); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void battery_hook_remove_battery(struct acpi_battery *battery) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct acpi_battery_hook *hook; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci mutex_lock(&hook_mutex); 7868c2ecf20Sopenharmony_ci /* 7878c2ecf20Sopenharmony_ci * Before removing the hook, we need to remove all 7888c2ecf20Sopenharmony_ci * custom attributes from the battery. 7898c2ecf20Sopenharmony_ci */ 7908c2ecf20Sopenharmony_ci list_for_each_entry(hook, &battery_hook_list, list) { 7918c2ecf20Sopenharmony_ci hook->remove_battery(battery->bat); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci /* Then, just remove the battery from the list */ 7948c2ecf20Sopenharmony_ci list_del(&battery->list); 7958c2ecf20Sopenharmony_ci mutex_unlock(&hook_mutex); 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic void __exit battery_hook_exit(void) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct acpi_battery_hook *hook; 8018c2ecf20Sopenharmony_ci struct acpi_battery_hook *ptr; 8028c2ecf20Sopenharmony_ci /* 8038c2ecf20Sopenharmony_ci * At this point, the acpi_bus_unregister_driver() 8048c2ecf20Sopenharmony_ci * has called remove for all batteries. We just 8058c2ecf20Sopenharmony_ci * need to remove the hooks. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci list_for_each_entry_safe(hook, ptr, &battery_hook_list, list) { 8088c2ecf20Sopenharmony_ci __battery_hook_unregister(hook, 1); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci mutex_destroy(&hook_mutex); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int sysfs_add_battery(struct acpi_battery *battery) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = { .drv_data = battery, }; 8168c2ecf20Sopenharmony_ci bool full_cap_broken = false; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (!ACPI_BATTERY_CAPACITY_VALID(battery->full_charge_capacity) && 8198c2ecf20Sopenharmony_ci !ACPI_BATTERY_CAPACITY_VALID(battery->design_capacity)) 8208c2ecf20Sopenharmony_ci full_cap_broken = true; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) { 8238c2ecf20Sopenharmony_ci if (full_cap_broken) { 8248c2ecf20Sopenharmony_ci battery->bat_desc.properties = 8258c2ecf20Sopenharmony_ci charge_battery_full_cap_broken_props; 8268c2ecf20Sopenharmony_ci battery->bat_desc.num_properties = 8278c2ecf20Sopenharmony_ci ARRAY_SIZE(charge_battery_full_cap_broken_props); 8288c2ecf20Sopenharmony_ci } else { 8298c2ecf20Sopenharmony_ci battery->bat_desc.properties = charge_battery_props; 8308c2ecf20Sopenharmony_ci battery->bat_desc.num_properties = 8318c2ecf20Sopenharmony_ci ARRAY_SIZE(charge_battery_props); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci } else { 8348c2ecf20Sopenharmony_ci if (full_cap_broken) { 8358c2ecf20Sopenharmony_ci battery->bat_desc.properties = 8368c2ecf20Sopenharmony_ci energy_battery_full_cap_broken_props; 8378c2ecf20Sopenharmony_ci battery->bat_desc.num_properties = 8388c2ecf20Sopenharmony_ci ARRAY_SIZE(energy_battery_full_cap_broken_props); 8398c2ecf20Sopenharmony_ci } else { 8408c2ecf20Sopenharmony_ci battery->bat_desc.properties = energy_battery_props; 8418c2ecf20Sopenharmony_ci battery->bat_desc.num_properties = 8428c2ecf20Sopenharmony_ci ARRAY_SIZE(energy_battery_props); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci battery->bat_desc.name = acpi_device_bid(battery->device); 8478c2ecf20Sopenharmony_ci battery->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; 8488c2ecf20Sopenharmony_ci battery->bat_desc.get_property = acpi_battery_get_property; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci battery->bat = power_supply_register_no_ws(&battery->device->dev, 8518c2ecf20Sopenharmony_ci &battery->bat_desc, &psy_cfg); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (IS_ERR(battery->bat)) { 8548c2ecf20Sopenharmony_ci int result = PTR_ERR(battery->bat); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci battery->bat = NULL; 8578c2ecf20Sopenharmony_ci return result; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci battery_hook_add_battery(battery); 8608c2ecf20Sopenharmony_ci return device_create_file(&battery->bat->dev, &alarm_attr); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic void sysfs_remove_battery(struct acpi_battery *battery) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci mutex_lock(&battery->sysfs_lock); 8668c2ecf20Sopenharmony_ci if (!battery->bat) { 8678c2ecf20Sopenharmony_ci mutex_unlock(&battery->sysfs_lock); 8688c2ecf20Sopenharmony_ci return; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci battery_hook_remove_battery(battery); 8718c2ecf20Sopenharmony_ci device_remove_file(&battery->bat->dev, &alarm_attr); 8728c2ecf20Sopenharmony_ci power_supply_unregister(battery->bat); 8738c2ecf20Sopenharmony_ci battery->bat = NULL; 8748c2ecf20Sopenharmony_ci mutex_unlock(&battery->sysfs_lock); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic void find_battery(const struct dmi_header *dm, void *private) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct acpi_battery *battery = (struct acpi_battery *)private; 8808c2ecf20Sopenharmony_ci /* Note: the hardcoded offsets below have been extracted from 8818c2ecf20Sopenharmony_ci the source code of dmidecode. */ 8828c2ecf20Sopenharmony_ci if (dm->type == DMI_ENTRY_PORTABLE_BATTERY && dm->length >= 8) { 8838c2ecf20Sopenharmony_ci const u8 *dmi_data = (const u8 *)(dm + 1); 8848c2ecf20Sopenharmony_ci int dmi_capacity = get_unaligned((const u16 *)(dmi_data + 6)); 8858c2ecf20Sopenharmony_ci if (dm->length >= 18) 8868c2ecf20Sopenharmony_ci dmi_capacity *= dmi_data[17]; 8878c2ecf20Sopenharmony_ci if (battery->design_capacity * battery->design_voltage / 1000 8888c2ecf20Sopenharmony_ci != dmi_capacity && 8898c2ecf20Sopenharmony_ci battery->design_capacity * 10 == dmi_capacity) 8908c2ecf20Sopenharmony_ci set_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, 8918c2ecf20Sopenharmony_ci &battery->flags); 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci/* 8968c2ecf20Sopenharmony_ci * According to the ACPI spec, some kinds of primary batteries can 8978c2ecf20Sopenharmony_ci * report percentage battery remaining capacity directly to OS. 8988c2ecf20Sopenharmony_ci * In this case, it reports the Last Full Charged Capacity == 100 8998c2ecf20Sopenharmony_ci * and BatteryPresentRate == 0xFFFFFFFF. 9008c2ecf20Sopenharmony_ci * 9018c2ecf20Sopenharmony_ci * Now we found some battery reports percentage remaining capacity 9028c2ecf20Sopenharmony_ci * even if it's rechargeable. 9038c2ecf20Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=15979 9048c2ecf20Sopenharmony_ci * 9058c2ecf20Sopenharmony_ci * Handle this correctly so that they won't break userspace. 9068c2ecf20Sopenharmony_ci */ 9078c2ecf20Sopenharmony_cistatic void acpi_battery_quirks(struct acpi_battery *battery) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) 9108c2ecf20Sopenharmony_ci return; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (battery->full_charge_capacity == 100 && 9138c2ecf20Sopenharmony_ci battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN && 9148c2ecf20Sopenharmony_ci battery->capacity_now >= 0 && battery->capacity_now <= 100) { 9158c2ecf20Sopenharmony_ci set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags); 9168c2ecf20Sopenharmony_ci battery->full_charge_capacity = battery->design_capacity; 9178c2ecf20Sopenharmony_ci battery->capacity_now = (battery->capacity_now * 9188c2ecf20Sopenharmony_ci battery->full_charge_capacity) / 100; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags)) 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (battery->power_unit && dmi_name_in_vendors("LENOVO")) { 9258c2ecf20Sopenharmony_ci const char *s; 9268c2ecf20Sopenharmony_ci s = dmi_get_system_info(DMI_PRODUCT_VERSION); 9278c2ecf20Sopenharmony_ci if (s && !strncasecmp(s, "ThinkPad", 8)) { 9288c2ecf20Sopenharmony_ci dmi_walk(find_battery, battery); 9298c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, 9308c2ecf20Sopenharmony_ci &battery->flags) && 9318c2ecf20Sopenharmony_ci battery->design_voltage) { 9328c2ecf20Sopenharmony_ci battery->design_capacity = 9338c2ecf20Sopenharmony_ci battery->design_capacity * 9348c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 9358c2ecf20Sopenharmony_ci battery->full_charge_capacity = 9368c2ecf20Sopenharmony_ci battery->full_charge_capacity * 9378c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 9388c2ecf20Sopenharmony_ci battery->design_capacity_warning = 9398c2ecf20Sopenharmony_ci battery->design_capacity_warning * 9408c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 9418c2ecf20Sopenharmony_ci battery->capacity_now = battery->capacity_now * 9428c2ecf20Sopenharmony_ci 10000 / battery->design_voltage; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (test_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags)) 9488c2ecf20Sopenharmony_ci return; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (acpi_battery_is_degraded(battery) && 9518c2ecf20Sopenharmony_ci battery->capacity_now > battery->full_charge_capacity) { 9528c2ecf20Sopenharmony_ci set_bit(ACPI_BATTERY_QUIRK_DEGRADED_FULL_CHARGE, &battery->flags); 9538c2ecf20Sopenharmony_ci battery->capacity_now = battery->full_charge_capacity; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic int acpi_battery_update(struct acpi_battery *battery, bool resume) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci int result = acpi_battery_get_status(battery); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (result) 9628c2ecf20Sopenharmony_ci return result; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (!acpi_battery_present(battery)) { 9658c2ecf20Sopenharmony_ci sysfs_remove_battery(battery); 9668c2ecf20Sopenharmony_ci battery->update_time = 0; 9678c2ecf20Sopenharmony_ci return 0; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (resume) 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (!battery->update_time) { 9748c2ecf20Sopenharmony_ci result = acpi_battery_get_info(battery); 9758c2ecf20Sopenharmony_ci if (result) 9768c2ecf20Sopenharmony_ci return result; 9778c2ecf20Sopenharmony_ci acpi_battery_init_alarm(battery); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci result = acpi_battery_get_state(battery); 9818c2ecf20Sopenharmony_ci if (result) 9828c2ecf20Sopenharmony_ci return result; 9838c2ecf20Sopenharmony_ci acpi_battery_quirks(battery); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!battery->bat) { 9868c2ecf20Sopenharmony_ci result = sysfs_add_battery(battery); 9878c2ecf20Sopenharmony_ci if (result) 9888c2ecf20Sopenharmony_ci return result; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* 9928c2ecf20Sopenharmony_ci * Wakeup the system if battery is critical low 9938c2ecf20Sopenharmony_ci * or lower than the alarm level 9948c2ecf20Sopenharmony_ci */ 9958c2ecf20Sopenharmony_ci if ((battery->state & ACPI_BATTERY_STATE_CRITICAL) || 9968c2ecf20Sopenharmony_ci (test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags) && 9978c2ecf20Sopenharmony_ci (battery->capacity_now <= battery->alarm))) 9988c2ecf20Sopenharmony_ci acpi_pm_wakeup_event(&battery->device->dev); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci return result; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void acpi_battery_refresh(struct acpi_battery *battery) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci int power_unit; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (!battery->bat) 10088c2ecf20Sopenharmony_ci return; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci power_unit = battery->power_unit; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci acpi_battery_get_info(battery); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci if (power_unit == battery->power_unit) 10158c2ecf20Sopenharmony_ci return; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* The battery has changed its reporting units. */ 10188c2ecf20Sopenharmony_ci sysfs_remove_battery(battery); 10198c2ecf20Sopenharmony_ci sysfs_add_battery(battery); 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 10238c2ecf20Sopenharmony_ci Driver Interface 10248c2ecf20Sopenharmony_ci -------------------------------------------------------------------------- */ 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic void acpi_battery_notify(struct acpi_device *device, u32 event) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct acpi_battery *battery = acpi_driver_data(device); 10298c2ecf20Sopenharmony_ci struct power_supply *old; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (!battery) 10328c2ecf20Sopenharmony_ci return; 10338c2ecf20Sopenharmony_ci old = battery->bat; 10348c2ecf20Sopenharmony_ci /* 10358c2ecf20Sopenharmony_ci * On Acer Aspire V5-573G notifications are sometimes triggered too 10368c2ecf20Sopenharmony_ci * early. For example, when AC is unplugged and notification is 10378c2ecf20Sopenharmony_ci * triggered, battery state is still reported as "Full", and changes to 10388c2ecf20Sopenharmony_ci * "Discharging" only after short delay, without any notification. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci if (battery_notification_delay_ms > 0) 10418c2ecf20Sopenharmony_ci msleep(battery_notification_delay_ms); 10428c2ecf20Sopenharmony_ci if (event == ACPI_BATTERY_NOTIFY_INFO) 10438c2ecf20Sopenharmony_ci acpi_battery_refresh(battery); 10448c2ecf20Sopenharmony_ci acpi_battery_update(battery, false); 10458c2ecf20Sopenharmony_ci acpi_bus_generate_netlink_event(device->pnp.device_class, 10468c2ecf20Sopenharmony_ci dev_name(&device->dev), event, 10478c2ecf20Sopenharmony_ci acpi_battery_present(battery)); 10488c2ecf20Sopenharmony_ci acpi_notifier_call_chain(device, event, acpi_battery_present(battery)); 10498c2ecf20Sopenharmony_ci /* acpi_battery_update could remove power_supply object */ 10508c2ecf20Sopenharmony_ci if (old && battery->bat) 10518c2ecf20Sopenharmony_ci power_supply_changed(battery->bat); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic int battery_notify(struct notifier_block *nb, 10558c2ecf20Sopenharmony_ci unsigned long mode, void *_unused) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci struct acpi_battery *battery = container_of(nb, struct acpi_battery, 10588c2ecf20Sopenharmony_ci pm_nb); 10598c2ecf20Sopenharmony_ci int result; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci switch (mode) { 10628c2ecf20Sopenharmony_ci case PM_POST_HIBERNATION: 10638c2ecf20Sopenharmony_ci case PM_POST_SUSPEND: 10648c2ecf20Sopenharmony_ci if (!acpi_battery_present(battery)) 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (battery->bat) { 10688c2ecf20Sopenharmony_ci acpi_battery_refresh(battery); 10698c2ecf20Sopenharmony_ci } else { 10708c2ecf20Sopenharmony_ci result = acpi_battery_get_info(battery); 10718c2ecf20Sopenharmony_ci if (result) 10728c2ecf20Sopenharmony_ci return result; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci result = sysfs_add_battery(battery); 10758c2ecf20Sopenharmony_ci if (result) 10768c2ecf20Sopenharmony_ci return result; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci acpi_battery_init_alarm(battery); 10808c2ecf20Sopenharmony_ci acpi_battery_get_state(battery); 10818c2ecf20Sopenharmony_ci break; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int __init 10888c2ecf20Sopenharmony_cibattery_bix_broken_package_quirk(const struct dmi_system_id *d) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci battery_bix_broken_package = 1; 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic int __init 10958c2ecf20Sopenharmony_cibattery_notification_delay_quirk(const struct dmi_system_id *d) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci battery_notification_delay_ms = 1000; 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic int __init 11028c2ecf20Sopenharmony_cibattery_ac_is_broken_quirk(const struct dmi_system_id *d) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci battery_ac_is_broken = 1; 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int __init 11098c2ecf20Sopenharmony_cibattery_do_not_check_pmic_quirk(const struct dmi_system_id *d) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci battery_check_pmic = 0; 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic int __init battery_quirk_not_charging(const struct dmi_system_id *d) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci battery_quirk_notcharging = 1; 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic const struct dmi_system_id bat_dmi_table[] __initconst = { 11228c2ecf20Sopenharmony_ci { 11238c2ecf20Sopenharmony_ci /* NEC LZ750/LS */ 11248c2ecf20Sopenharmony_ci .callback = battery_bix_broken_package_quirk, 11258c2ecf20Sopenharmony_ci .matches = { 11268c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "NEC"), 11278c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"), 11288c2ecf20Sopenharmony_ci }, 11298c2ecf20Sopenharmony_ci }, 11308c2ecf20Sopenharmony_ci { 11318c2ecf20Sopenharmony_ci /* Acer Aspire V5-573G */ 11328c2ecf20Sopenharmony_ci .callback = battery_notification_delay_quirk, 11338c2ecf20Sopenharmony_ci .matches = { 11348c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 11358c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-573G"), 11368c2ecf20Sopenharmony_ci }, 11378c2ecf20Sopenharmony_ci }, 11388c2ecf20Sopenharmony_ci { 11398c2ecf20Sopenharmony_ci /* Point of View mobii wintab p800w */ 11408c2ecf20Sopenharmony_ci .callback = battery_ac_is_broken_quirk, 11418c2ecf20Sopenharmony_ci .matches = { 11428c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), 11438c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), 11448c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"), 11458c2ecf20Sopenharmony_ci /* Above matches are too generic, add bios-date match */ 11468c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"), 11478c2ecf20Sopenharmony_ci }, 11488c2ecf20Sopenharmony_ci }, 11498c2ecf20Sopenharmony_ci { 11508c2ecf20Sopenharmony_ci /* ECS EF20EA, AXP288 PMIC but uses separate fuel-gauge */ 11518c2ecf20Sopenharmony_ci .callback = battery_do_not_check_pmic_quirk, 11528c2ecf20Sopenharmony_ci .matches = { 11538c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), 11548c2ecf20Sopenharmony_ci }, 11558c2ecf20Sopenharmony_ci }, 11568c2ecf20Sopenharmony_ci { 11578c2ecf20Sopenharmony_ci /* Lenovo Ideapad Miix 320, AXP288 PMIC, separate fuel-gauge */ 11588c2ecf20Sopenharmony_ci .callback = battery_do_not_check_pmic_quirk, 11598c2ecf20Sopenharmony_ci .matches = { 11608c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 11618c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "80XF"), 11628c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), 11638c2ecf20Sopenharmony_ci }, 11648c2ecf20Sopenharmony_ci }, 11658c2ecf20Sopenharmony_ci { 11668c2ecf20Sopenharmony_ci /* 11678c2ecf20Sopenharmony_ci * On Lenovo ThinkPads the BIOS specification defines 11688c2ecf20Sopenharmony_ci * a state when the bits for charging and discharging 11698c2ecf20Sopenharmony_ci * are both set to 0. That state is "Not Charging". 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci .callback = battery_quirk_not_charging, 11728c2ecf20Sopenharmony_ci .ident = "Lenovo ThinkPad", 11738c2ecf20Sopenharmony_ci .matches = { 11748c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 11758c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"), 11768c2ecf20Sopenharmony_ci }, 11778c2ecf20Sopenharmony_ci }, 11788c2ecf20Sopenharmony_ci { 11798c2ecf20Sopenharmony_ci /* Microsoft Surface Go 3 */ 11808c2ecf20Sopenharmony_ci .callback = battery_notification_delay_quirk, 11818c2ecf20Sopenharmony_ci .matches = { 11828c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 11838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), 11848c2ecf20Sopenharmony_ci }, 11858c2ecf20Sopenharmony_ci }, 11868c2ecf20Sopenharmony_ci {}, 11878c2ecf20Sopenharmony_ci}; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci/* 11908c2ecf20Sopenharmony_ci * Some machines'(E,G Lenovo Z480) ECs are not stable 11918c2ecf20Sopenharmony_ci * during boot up and this causes battery driver fails to be 11928c2ecf20Sopenharmony_ci * probed due to failure of getting battery information 11938c2ecf20Sopenharmony_ci * from EC sometimes. After several retries, the operation 11948c2ecf20Sopenharmony_ci * may work. So add retry code here and 20ms sleep between 11958c2ecf20Sopenharmony_ci * every retries. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_cistatic int acpi_battery_update_retry(struct acpi_battery *battery) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci int retry, ret; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci for (retry = 5; retry; retry--) { 12028c2ecf20Sopenharmony_ci ret = acpi_battery_update(battery, false); 12038c2ecf20Sopenharmony_ci if (!ret) 12048c2ecf20Sopenharmony_ci break; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci msleep(20); 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci return ret; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic int acpi_battery_add(struct acpi_device *device) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci int result = 0; 12148c2ecf20Sopenharmony_ci struct acpi_battery *battery = NULL; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (!device) 12178c2ecf20Sopenharmony_ci return -EINVAL; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (device->dep_unmet) 12208c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); 12238c2ecf20Sopenharmony_ci if (!battery) 12248c2ecf20Sopenharmony_ci return -ENOMEM; 12258c2ecf20Sopenharmony_ci battery->device = device; 12268c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); 12278c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); 12288c2ecf20Sopenharmony_ci device->driver_data = battery; 12298c2ecf20Sopenharmony_ci mutex_init(&battery->lock); 12308c2ecf20Sopenharmony_ci mutex_init(&battery->sysfs_lock); 12318c2ecf20Sopenharmony_ci if (acpi_has_method(battery->device->handle, "_BIX")) 12328c2ecf20Sopenharmony_ci set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci result = acpi_battery_update_retry(battery); 12358c2ecf20Sopenharmony_ci if (result) 12368c2ecf20Sopenharmony_ci goto fail; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci pr_info(PREFIX "%s Slot [%s] (battery %s)\n", 12398c2ecf20Sopenharmony_ci ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), 12408c2ecf20Sopenharmony_ci device->status.battery_present ? "present" : "absent"); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci battery->pm_nb.notifier_call = battery_notify; 12438c2ecf20Sopenharmony_ci register_pm_notifier(&battery->pm_nb); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci device_init_wakeup(&device->dev, 1); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return result; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cifail: 12508c2ecf20Sopenharmony_ci sysfs_remove_battery(battery); 12518c2ecf20Sopenharmony_ci mutex_destroy(&battery->lock); 12528c2ecf20Sopenharmony_ci mutex_destroy(&battery->sysfs_lock); 12538c2ecf20Sopenharmony_ci kfree(battery); 12548c2ecf20Sopenharmony_ci return result; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int acpi_battery_remove(struct acpi_device *device) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct acpi_battery *battery = NULL; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (!device || !acpi_driver_data(device)) 12628c2ecf20Sopenharmony_ci return -EINVAL; 12638c2ecf20Sopenharmony_ci device_init_wakeup(&device->dev, 0); 12648c2ecf20Sopenharmony_ci battery = acpi_driver_data(device); 12658c2ecf20Sopenharmony_ci unregister_pm_notifier(&battery->pm_nb); 12668c2ecf20Sopenharmony_ci sysfs_remove_battery(battery); 12678c2ecf20Sopenharmony_ci mutex_destroy(&battery->lock); 12688c2ecf20Sopenharmony_ci mutex_destroy(&battery->sysfs_lock); 12698c2ecf20Sopenharmony_ci kfree(battery); 12708c2ecf20Sopenharmony_ci return 0; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12748c2ecf20Sopenharmony_ci/* this is needed to learn about changes made in suspended state */ 12758c2ecf20Sopenharmony_cistatic int acpi_battery_resume(struct device *dev) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct acpi_battery *battery; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (!dev) 12808c2ecf20Sopenharmony_ci return -EINVAL; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci battery = acpi_driver_data(to_acpi_device(dev)); 12838c2ecf20Sopenharmony_ci if (!battery) 12848c2ecf20Sopenharmony_ci return -EINVAL; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci battery->update_time = 0; 12878c2ecf20Sopenharmony_ci acpi_battery_update(battery, true); 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci#else 12918c2ecf20Sopenharmony_ci#define acpi_battery_resume NULL 12928c2ecf20Sopenharmony_ci#endif 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_battery_driver = { 12978c2ecf20Sopenharmony_ci .name = "battery", 12988c2ecf20Sopenharmony_ci .class = ACPI_BATTERY_CLASS, 12998c2ecf20Sopenharmony_ci .ids = battery_device_ids, 13008c2ecf20Sopenharmony_ci .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 13018c2ecf20Sopenharmony_ci .ops = { 13028c2ecf20Sopenharmony_ci .add = acpi_battery_add, 13038c2ecf20Sopenharmony_ci .remove = acpi_battery_remove, 13048c2ecf20Sopenharmony_ci .notify = acpi_battery_notify, 13058c2ecf20Sopenharmony_ci }, 13068c2ecf20Sopenharmony_ci .drv.pm = &acpi_battery_pm, 13078c2ecf20Sopenharmony_ci}; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci unsigned int i; 13128c2ecf20Sopenharmony_ci int result; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci dmi_check_system(bat_dmi_table); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (battery_check_pmic) { 13178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(acpi_battery_blacklist); i++) 13188c2ecf20Sopenharmony_ci if (acpi_dev_present(acpi_battery_blacklist[i], "1", -1)) { 13198c2ecf20Sopenharmony_ci pr_info(PREFIX ACPI_BATTERY_DEVICE_NAME 13208c2ecf20Sopenharmony_ci ": found native %s PMIC, not loading\n", 13218c2ecf20Sopenharmony_ci acpi_battery_blacklist[i]); 13228c2ecf20Sopenharmony_ci return; 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci result = acpi_bus_register_driver(&acpi_battery_driver); 13278c2ecf20Sopenharmony_ci battery_driver_registered = (result == 0); 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic int __init acpi_battery_init(void) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci if (acpi_disabled) 13338c2ecf20Sopenharmony_ci return -ENODEV; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci async_cookie = async_schedule(acpi_battery_init_async, NULL); 13368c2ecf20Sopenharmony_ci return 0; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic void __exit acpi_battery_exit(void) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci async_synchronize_cookie(async_cookie + 1); 13428c2ecf20Sopenharmony_ci if (battery_driver_registered) { 13438c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&acpi_battery_driver); 13448c2ecf20Sopenharmony_ci battery_hook_exit(); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cimodule_init(acpi_battery_init); 13498c2ecf20Sopenharmony_cimodule_exit(acpi_battery_exit); 1350