1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 39104457eSXiaofei Tan * acpi_ac.c - ACPI AC Adapter Driver (Revision: 27) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 61da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 92249ff34SRafael J. Wysocki #define pr_fmt(fmt) "ACPI: AC: " fmt 102249ff34SRafael J. Wysocki 111da177e4SLinus Torvalds #include <linux/kernel.h> 121da177e4SLinus Torvalds #include <linux/module.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 141da177e4SLinus Torvalds #include <linux/init.h> 151da177e4SLinus Torvalds #include <linux/types.h> 160ab5bb64SLan Tianyu #include <linux/dmi.h> 170ab5bb64SLan Tianyu #include <linux/delay.h> 18cc8ef527SZhang Rui #include <linux/platform_device.h> 19d5b4a3d0SAlexey Starikovskiy #include <linux/power_supply.h> 208b48463fSLv Zheng #include <linux/acpi.h> 21fa93854fSOgnjen Galic #include <acpi/battery.h> 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #define ACPI_AC_CLASS "ac_adapter" 241da177e4SLinus Torvalds #define ACPI_AC_DEVICE_NAME "AC Adapter" 251da177e4SLinus Torvalds #define ACPI_AC_FILE_STATE "state" 261da177e4SLinus Torvalds #define ACPI_AC_NOTIFY_STATUS 0x80 271da177e4SLinus Torvalds #define ACPI_AC_STATUS_OFFLINE 0x00 281da177e4SLinus Torvalds #define ACPI_AC_STATUS_ONLINE 0x01 291da177e4SLinus Torvalds #define ACPI_AC_STATUS_UNKNOWN 0xFF 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds MODULE_AUTHOR("Paul Diefenbaugh"); 327cda93e0SLen Brown MODULE_DESCRIPTION("ACPI AC Adapter Driver"); 331da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 341da177e4SLinus Torvalds 3598012849SGuenter Roeck static int acpi_ac_add(struct acpi_device *device); 366c0eb5baSDawei Li static void acpi_ac_remove(struct acpi_device *device); 37*543a2d11SMichal Wilczynski static void acpi_ac_notify(acpi_handle handle, u32 event, void *data); 3898012849SGuenter Roeck 3998012849SGuenter Roeck static const struct acpi_device_id ac_device_ids[] = { 4098012849SGuenter Roeck {"ACPI0003", 0}, 4198012849SGuenter Roeck {"", 0}, 4298012849SGuenter Roeck }; 4398012849SGuenter Roeck MODULE_DEVICE_TABLE(acpi, ac_device_ids); 4498012849SGuenter Roeck 4598012849SGuenter Roeck #ifdef CONFIG_PM_SLEEP 4698012849SGuenter Roeck static int acpi_ac_resume(struct device *dev); 4798012849SGuenter Roeck #endif 4898012849SGuenter Roeck static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); 4998012849SGuenter Roeck 500ab5bb64SLan Tianyu static int ac_sleep_before_get_state_ms; 513d730ee6SStefan Schaeckeler static int ac_only; 520ab5bb64SLan Tianyu 5398012849SGuenter Roeck static struct acpi_driver acpi_ac_driver = { 5498012849SGuenter Roeck .name = "ac", 5598012849SGuenter Roeck .class = ACPI_AC_CLASS, 5698012849SGuenter Roeck .ids = ac_device_ids, 5798012849SGuenter Roeck .ops = { 5898012849SGuenter Roeck .add = acpi_ac_add, 5998012849SGuenter Roeck .remove = acpi_ac_remove, 6098012849SGuenter Roeck }, 6198012849SGuenter Roeck .drv.pm = &acpi_ac_pm, 6298012849SGuenter Roeck }; 6398012849SGuenter Roeck 641da177e4SLinus Torvalds struct acpi_ac { 65297d716fSKrzysztof Kozlowski struct power_supply *charger; 66297d716fSKrzysztof Kozlowski struct power_supply_desc charger_desc; 6798012849SGuenter Roeck struct acpi_device *device; 6827663c58SMatthew Wilcox unsigned long long state; 694eee4f03SAlexander Mezin struct notifier_block battery_nb; 701da177e4SLinus Torvalds }; 711da177e4SLinus Torvalds 72297d716fSKrzysztof Kozlowski #define to_acpi_ac(x) power_supply_get_drvdata(x) 73d5b4a3d0SAlexey Starikovskiy 749104457eSXiaofei Tan /* AC Adapter Management */ 754be44fcdSLen Brown static int acpi_ac_get_state(struct acpi_ac *ac) 761da177e4SLinus Torvalds { 7798012849SGuenter Roeck acpi_status status = AE_OK; 781da177e4SLinus Torvalds 7998012849SGuenter Roeck if (!ac) 8098012849SGuenter Roeck return -EINVAL; 8198012849SGuenter Roeck 823d730ee6SStefan Schaeckeler if (ac_only) { 833d730ee6SStefan Schaeckeler ac->state = 1; 843d730ee6SStefan Schaeckeler return 0; 853d730ee6SStefan Schaeckeler } 863d730ee6SStefan Schaeckeler 8798012849SGuenter Roeck status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, 88cc8ef527SZhang Rui &ac->state); 891da177e4SLinus Torvalds if (ACPI_FAILURE(status)) { 902249ff34SRafael J. Wysocki acpi_handle_info(ac->device->handle, 912249ff34SRafael J. Wysocki "Error reading AC Adapter state: %s\n", 922249ff34SRafael J. Wysocki acpi_format_exception(status)); 931da177e4SLinus Torvalds ac->state = ACPI_AC_STATUS_UNKNOWN; 94d550d98dSPatrick Mochel return -ENODEV; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 97d550d98dSPatrick Mochel return 0; 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds 1009104457eSXiaofei Tan /* sysfs I/F */ 1013151dbb0SZhang Rui static int get_ac_property(struct power_supply *psy, 1023151dbb0SZhang Rui enum power_supply_property psp, 1033151dbb0SZhang Rui union power_supply_propval *val) 1043151dbb0SZhang Rui { 1053151dbb0SZhang Rui struct acpi_ac *ac = to_acpi_ac(psy); 1063151dbb0SZhang Rui 1073151dbb0SZhang Rui if (!ac) 1083151dbb0SZhang Rui return -ENODEV; 1093151dbb0SZhang Rui 1103151dbb0SZhang Rui if (acpi_ac_get_state(ac)) 1113151dbb0SZhang Rui return -ENODEV; 1123151dbb0SZhang Rui 1133151dbb0SZhang Rui switch (psp) { 1143151dbb0SZhang Rui case POWER_SUPPLY_PROP_ONLINE: 1153151dbb0SZhang Rui val->intval = ac->state; 1163151dbb0SZhang Rui break; 1173151dbb0SZhang Rui default: 1183151dbb0SZhang Rui return -EINVAL; 1193151dbb0SZhang Rui } 1204c19851cSIan Cowan 1213151dbb0SZhang Rui return 0; 1223151dbb0SZhang Rui } 1233151dbb0SZhang Rui 1243151dbb0SZhang Rui static enum power_supply_property ac_props[] = { 1253151dbb0SZhang Rui POWER_SUPPLY_PROP_ONLINE, 1263151dbb0SZhang Rui }; 1273151dbb0SZhang Rui 1289104457eSXiaofei Tan /* Driver Model */ 129*543a2d11SMichal Wilczynski static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) 1301da177e4SLinus Torvalds { 131*543a2d11SMichal Wilczynski struct acpi_device *device = data; 13298012849SGuenter Roeck struct acpi_ac *ac = acpi_driver_data(device); 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds if (!ac) 135d550d98dSPatrick Mochel return; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds switch (event) { 138f163ff51SLen Brown default: 1392249ff34SRafael J. Wysocki acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", 1402249ff34SRafael J. Wysocki event); 14157d2dd4bSGustavo A. R. Silva fallthrough; 1421da177e4SLinus Torvalds case ACPI_AC_NOTIFY_STATUS: 14303d78252SChristian Lupien case ACPI_NOTIFY_BUS_CHECK: 14403d78252SChristian Lupien case ACPI_NOTIFY_DEVICE_CHECK: 1450ab5bb64SLan Tianyu /* 1460ab5bb64SLan Tianyu * A buggy BIOS may notify AC first and then sleep for 1470ab5bb64SLan Tianyu * a specific time before doing actual operations in the 1480ab5bb64SLan Tianyu * EC event handler (_Qxx). This will cause the AC state 1490ab5bb64SLan Tianyu * reported by the ACPI event to be incorrect, so wait for a 1500ab5bb64SLan Tianyu * specific time for the EC event handler to make progress. 1510ab5bb64SLan Tianyu */ 1520ab5bb64SLan Tianyu if (ac_sleep_before_get_state_ms > 0) 1530ab5bb64SLan Tianyu msleep(ac_sleep_before_get_state_ms); 1540ab5bb64SLan Tianyu 1551da177e4SLinus Torvalds acpi_ac_get_state(ac); 15698012849SGuenter Roeck acpi_bus_generate_netlink_event(device->pnp.device_class, 15798012849SGuenter Roeck dev_name(&device->dev), event, 15898012849SGuenter Roeck (u32) ac->state); 15998012849SGuenter Roeck acpi_notifier_call_chain(device, event, (u32) ac->state); 160297d716fSKrzysztof Kozlowski kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1644eee4f03SAlexander Mezin static int acpi_ac_battery_notify(struct notifier_block *nb, 1654eee4f03SAlexander Mezin unsigned long action, void *data) 1664eee4f03SAlexander Mezin { 1674eee4f03SAlexander Mezin struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb); 1684eee4f03SAlexander Mezin struct acpi_bus_event *event = (struct acpi_bus_event *)data; 1694eee4f03SAlexander Mezin 1704eee4f03SAlexander Mezin /* 1714eee4f03SAlexander Mezin * On HP Pavilion dv6-6179er AC status notifications aren't triggered 1724eee4f03SAlexander Mezin * when adapter is plugged/unplugged. However, battery status 173935ab850STom Saeger * notifications are triggered when battery starts charging or 1744eee4f03SAlexander Mezin * discharging. Re-reading AC status triggers lost AC notifications, 1754eee4f03SAlexander Mezin * if AC status has changed. 1764eee4f03SAlexander Mezin */ 1774eee4f03SAlexander Mezin if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 && 1784eee4f03SAlexander Mezin event->type == ACPI_BATTERY_NOTIFY_STATUS) 1794eee4f03SAlexander Mezin acpi_ac_get_state(ac); 1804eee4f03SAlexander Mezin 1814eee4f03SAlexander Mezin return NOTIFY_OK; 1824eee4f03SAlexander Mezin } 1834eee4f03SAlexander Mezin 18491ea5b1dSCarlo Caione static int __init thinkpad_e530_quirk(const struct dmi_system_id *d) 1850ab5bb64SLan Tianyu { 1860ab5bb64SLan Tianyu ac_sleep_before_get_state_ms = 1000; 1870ab5bb64SLan Tianyu return 0; 1880ab5bb64SLan Tianyu } 1890ab5bb64SLan Tianyu 1903d730ee6SStefan Schaeckeler static int __init ac_only_quirk(const struct dmi_system_id *d) 1913d730ee6SStefan Schaeckeler { 1923d730ee6SStefan Schaeckeler ac_only = 1; 1933d730ee6SStefan Schaeckeler return 0; 1943d730ee6SStefan Schaeckeler } 1953d730ee6SStefan Schaeckeler 19604900fa3SHans de Goede /* Please keep this list alphabetically sorted */ 19791ea5b1dSCarlo Caione static const struct dmi_system_id ac_dmi_table[] __initconst = { 1980ab5bb64SLan Tianyu { 1993d730ee6SStefan Schaeckeler /* Kodlix GK45 returning incorrect state */ 2003d730ee6SStefan Schaeckeler .callback = ac_only_quirk, 2013d730ee6SStefan Schaeckeler .matches = { 2023d730ee6SStefan Schaeckeler DMI_MATCH(DMI_PRODUCT_NAME, "GK45"), 2033d730ee6SStefan Schaeckeler }, 2043d730ee6SStefan Schaeckeler }, 2053d730ee6SStefan Schaeckeler { 20604900fa3SHans de Goede /* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */ 20704900fa3SHans de Goede .callback = thinkpad_e530_quirk, 20804900fa3SHans de Goede .matches = { 20904900fa3SHans de Goede DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 21004900fa3SHans de Goede DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), 21191ea5b1dSCarlo Caione }, 21291ea5b1dSCarlo Caione }, 2130ab5bb64SLan Tianyu {}, 2140ab5bb64SLan Tianyu }; 2150ab5bb64SLan Tianyu 21698012849SGuenter Roeck static int acpi_ac_add(struct acpi_device *device) 2171da177e4SLinus Torvalds { 218297d716fSKrzysztof Kozlowski struct power_supply_config psy_cfg = {}; 2191da177e4SLinus Torvalds int result = 0; 2201da177e4SLinus Torvalds struct acpi_ac *ac = NULL; 2211da177e4SLinus Torvalds 22298012849SGuenter Roeck 22398012849SGuenter Roeck if (!device) 224d550d98dSPatrick Mochel return -EINVAL; 2251da177e4SLinus Torvalds 22636bcbec7SBurman Yan ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); 2271da177e4SLinus Torvalds if (!ac) 228d550d98dSPatrick Mochel return -ENOMEM; 2291da177e4SLinus Torvalds 23098012849SGuenter Roeck ac->device = device; 23198012849SGuenter Roeck strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME); 23298012849SGuenter Roeck strcpy(acpi_device_class(device), ACPI_AC_CLASS); 23398012849SGuenter Roeck device->driver_data = ac; 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds result = acpi_ac_get_state(ac); 2361da177e4SLinus Torvalds if (result) 237*543a2d11SMichal Wilczynski goto err_release_ac; 2381da177e4SLinus Torvalds 239297d716fSKrzysztof Kozlowski psy_cfg.drv_data = ac; 240297d716fSKrzysztof Kozlowski 241297d716fSKrzysztof Kozlowski ac->charger_desc.name = acpi_device_bid(device); 242297d716fSKrzysztof Kozlowski ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS; 243297d716fSKrzysztof Kozlowski ac->charger_desc.properties = ac_props; 244297d716fSKrzysztof Kozlowski ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); 245297d716fSKrzysztof Kozlowski ac->charger_desc.get_property = get_ac_property; 246297d716fSKrzysztof Kozlowski ac->charger = power_supply_register(&ac->device->dev, 247297d716fSKrzysztof Kozlowski &ac->charger_desc, &psy_cfg); 248297d716fSKrzysztof Kozlowski if (IS_ERR(ac->charger)) { 249297d716fSKrzysztof Kozlowski result = PTR_ERR(ac->charger); 250*543a2d11SMichal Wilczynski goto err_release_ac; 251297d716fSKrzysztof Kozlowski } 2521da177e4SLinus Torvalds 2532249ff34SRafael J. Wysocki pr_info("%s [%s] (%s)\n", acpi_device_name(device), 2542249ff34SRafael J. Wysocki acpi_device_bid(device), ac->state ? "on-line" : "off-line"); 2551da177e4SLinus Torvalds 2564eee4f03SAlexander Mezin ac->battery_nb.notifier_call = acpi_ac_battery_notify; 2574eee4f03SAlexander Mezin register_acpi_notifier(&ac->battery_nb); 258*543a2d11SMichal Wilczynski 259*543a2d11SMichal Wilczynski result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, 260*543a2d11SMichal Wilczynski acpi_ac_notify); 2619104457eSXiaofei Tan if (result) 262*543a2d11SMichal Wilczynski goto err_unregister; 263*543a2d11SMichal Wilczynski 264*543a2d11SMichal Wilczynski return 0; 265*543a2d11SMichal Wilczynski 266*543a2d11SMichal Wilczynski err_unregister: 267*543a2d11SMichal Wilczynski power_supply_unregister(ac->charger); 268*543a2d11SMichal Wilczynski unregister_acpi_notifier(&ac->battery_nb); 269*543a2d11SMichal Wilczynski err_release_ac: 2701da177e4SLinus Torvalds kfree(ac); 2711da177e4SLinus Torvalds 272d550d98dSPatrick Mochel return result; 2731da177e4SLinus Torvalds } 2741da177e4SLinus Torvalds 27590692404SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 276ccda7069SRafael J. Wysocki static int acpi_ac_resume(struct device *dev) 2775bfeca31SAlexey Starikovskiy { 2785bfeca31SAlexey Starikovskiy struct acpi_ac *ac; 2799104457eSXiaofei Tan unsigned int old_state; 280ccda7069SRafael J. Wysocki 281ccda7069SRafael J. Wysocki if (!dev) 2825bfeca31SAlexey Starikovskiy return -EINVAL; 283ccda7069SRafael J. Wysocki 28498012849SGuenter Roeck ac = acpi_driver_data(to_acpi_device(dev)); 285ccda7069SRafael J. Wysocki if (!ac) 286ccda7069SRafael J. Wysocki return -EINVAL; 287ccda7069SRafael J. Wysocki 2885bfeca31SAlexey Starikovskiy old_state = ac->state; 2895bfeca31SAlexey Starikovskiy if (acpi_ac_get_state(ac)) 2905bfeca31SAlexey Starikovskiy return 0; 2915bfeca31SAlexey Starikovskiy if (old_state != ac->state) 292297d716fSKrzysztof Kozlowski kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); 2934c19851cSIan Cowan 2945bfeca31SAlexey Starikovskiy return 0; 2955bfeca31SAlexey Starikovskiy } 29606521c2eSShuah Khan #else 29706521c2eSShuah Khan #define acpi_ac_resume NULL 29890692404SRafael J. Wysocki #endif 2995bfeca31SAlexey Starikovskiy 3006c0eb5baSDawei Li static void acpi_ac_remove(struct acpi_device *device) 3011da177e4SLinus Torvalds { 30298012849SGuenter Roeck struct acpi_ac *ac = NULL; 3031da177e4SLinus Torvalds 30498012849SGuenter Roeck if (!device || !acpi_driver_data(device)) 3056c0eb5baSDawei Li return; 3061da177e4SLinus Torvalds 30798012849SGuenter Roeck ac = acpi_driver_data(device); 3081da177e4SLinus Torvalds 309*543a2d11SMichal Wilczynski acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY, 310*543a2d11SMichal Wilczynski acpi_ac_notify); 311297d716fSKrzysztof Kozlowski power_supply_unregister(ac->charger); 3124eee4f03SAlexander Mezin unregister_acpi_notifier(&ac->battery_nb); 313cc8ef527SZhang Rui 3141da177e4SLinus Torvalds kfree(ac); 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds 3174be44fcdSLen Brown static int __init acpi_ac_init(void) 3181da177e4SLinus Torvalds { 3193f86b832SRich Townsend int result; 3201da177e4SLinus Torvalds 3214d8316d5SPavel Machek if (acpi_disabled) 3224d8316d5SPavel Machek return -ENODEV; 3231da177e4SLinus Torvalds 32457a18322SHans de Goede if (acpi_quirk_skip_acpi_ac_and_battery()) 325af3ec837SHans de Goede return -ENODEV; 32657a18322SHans de Goede 32757a18322SHans de Goede dmi_check_system(ac_dmi_table); 328af3ec837SHans de Goede 329e63f6e28SLan Tianyu result = acpi_bus_register_driver(&acpi_ac_driver); 3309104457eSXiaofei Tan if (result < 0) 331e63f6e28SLan Tianyu return -ENODEV; 3321da177e4SLinus Torvalds 333d550d98dSPatrick Mochel return 0; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3364be44fcdSLen Brown static void __exit acpi_ac_exit(void) 3371da177e4SLinus Torvalds { 33898012849SGuenter Roeck acpi_bus_unregister_driver(&acpi_ac_driver); 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds module_init(acpi_ac_init); 3411da177e4SLinus Torvalds module_exit(acpi_ac_exit); 342