diff options
Diffstat (limited to 'drivers/acpi/ac.c')
| -rw-r--r-- | drivers/acpi/ac.c | 353 |
1 files changed, 95 insertions, 258 deletions
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index cdd3136829f1..1f69be8f51a2 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -1,24 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $) + * acpi_ac.c - ACPI AC Adapter Driver (Revision: 27) * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) "ACPI: AC: " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -26,17 +15,12 @@ #include <linux/types.h> #include <linux/dmi.h> #include <linux/delay.h> -#ifdef CONFIG_ACPI_PROCFS_POWER -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#endif #include <linux/platform_device.h> #include <linux/power_supply.h> +#include <linux/string_choices.h> #include <linux/acpi.h> #include <acpi/battery.h> -#define PREFIX "ACPI: " - #define ACPI_AC_CLASS "ac_adapter" #define ACPI_AC_DEVICE_NAME "AC Adapter" #define ACPI_AC_FILE_STATE "state" @@ -45,22 +29,14 @@ #define ACPI_AC_STATUS_ONLINE 0x01 #define ACPI_AC_STATUS_UNKNOWN 0xFF -#define _COMPONENT ACPI_AC_COMPONENT -ACPI_MODULE_NAME("ac"); - MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI AC Adapter Driver"); MODULE_LICENSE("GPL"); +static int acpi_ac_probe(struct platform_device *pdev); +static void acpi_ac_remove(struct platform_device *pdev); -static int acpi_ac_add(struct acpi_device *device); -static int acpi_ac_remove(struct acpi_device *device); -static void acpi_ac_notify(struct acpi_device *device, u32 event); - -struct acpi_ac_bl { - const char *hid; - int hrv; -}; +static void acpi_ac_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id ac_device_ids[] = { {"ACPI0003", 0}, @@ -68,53 +44,25 @@ static const struct acpi_device_id ac_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, ac_device_ids); -/* Lists of PMIC ACPI HIDs with an (often better) native charger driver */ -static const struct acpi_ac_bl acpi_ac_blacklist[] = { - { "INT33F4", -1 }, /* X-Powers AXP288 PMIC */ - { "INT34D3", 3 }, /* Intel Cherrytrail Whiskey Cove PMIC */ -}; - #ifdef CONFIG_PM_SLEEP static int acpi_ac_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); -#ifdef CONFIG_ACPI_PROCFS_POWER -extern struct proc_dir_entry *acpi_lock_ac_dir(void); -extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); -#endif - - static int ac_sleep_before_get_state_ms; -static int ac_check_pmic = 1; - -static struct acpi_driver acpi_ac_driver = { - .name = "ac", - .class = ACPI_AC_CLASS, - .ids = ac_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = acpi_ac_add, - .remove = acpi_ac_remove, - .notify = acpi_ac_notify, - }, - .drv.pm = &acpi_ac_pm, -}; +static int ac_only; struct acpi_ac { struct power_supply *charger; struct power_supply_desc charger_desc; - struct acpi_device * device; + struct acpi_device *device; unsigned long long state; struct notifier_block battery_nb; }; #define to_acpi_ac(x) power_supply_get_drvdata(x) -/* -------------------------------------------------------------------------- - AC Adapter Management - -------------------------------------------------------------------------- */ - +/* AC Adapter Management */ static int acpi_ac_get_state(struct acpi_ac *ac) { acpi_status status = AE_OK; @@ -122,11 +70,17 @@ static int acpi_ac_get_state(struct acpi_ac *ac) if (!ac) return -EINVAL; + if (ac_only) { + ac->state = 1; + return 0; + } + status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, &ac->state); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Error reading AC Adapter state")); + acpi_handle_info(ac->device->handle, + "Error reading AC Adapter state: %s\n", + acpi_format_exception(status)); ac->state = ACPI_AC_STATUS_UNKNOWN; return -ENODEV; } @@ -134,9 +88,7 @@ static int acpi_ac_get_state(struct acpi_ac *ac) return 0; } -/* -------------------------------------------------------------------------- - sysfs I/F - -------------------------------------------------------------------------- */ +/* sysfs I/F */ static int get_ac_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -156,100 +108,25 @@ static int get_ac_property(struct power_supply *psy, default: return -EINVAL; } + return 0; } -static enum power_supply_property ac_props[] = { +static const enum power_supply_property ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -#ifdef CONFIG_ACPI_PROCFS_POWER -/* -------------------------------------------------------------------------- - FS Interface (/proc) - -------------------------------------------------------------------------- */ - -static struct proc_dir_entry *acpi_ac_dir; - -static int acpi_ac_seq_show(struct seq_file *seq, void *offset) +/* Driver Model */ +static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_ac *ac = seq->private; - - - if (!ac) - return 0; - - if (acpi_ac_get_state(ac)) { - seq_puts(seq, "ERROR: Unable to read AC Adapter state\n"); - return 0; - } - - seq_puts(seq, "state: "); - switch (ac->state) { - case ACPI_AC_STATUS_OFFLINE: - seq_puts(seq, "off-line\n"); - break; - case ACPI_AC_STATUS_ONLINE: - seq_puts(seq, "on-line\n"); - break; - default: - seq_puts(seq, "unknown\n"); - break; - } - - return 0; -} - -static int acpi_ac_add_fs(struct acpi_ac *ac) -{ - struct proc_dir_entry *entry = NULL; - - printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded," - " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); - if (!acpi_device_dir(ac->device)) { - acpi_device_dir(ac->device) = - proc_mkdir(acpi_device_bid(ac->device), acpi_ac_dir); - if (!acpi_device_dir(ac->device)) - return -ENODEV; - } - - /* 'state' [R] */ - entry = proc_create_single_data(ACPI_AC_FILE_STATE, S_IRUGO, - acpi_device_dir(ac->device), acpi_ac_seq_show, ac); - if (!entry) - return -ENODEV; - return 0; -} - -static int acpi_ac_remove_fs(struct acpi_ac *ac) -{ - - if (acpi_device_dir(ac->device)) { - remove_proc_entry(ACPI_AC_FILE_STATE, - acpi_device_dir(ac->device)); - remove_proc_entry(acpi_device_bid(ac->device), acpi_ac_dir); - acpi_device_dir(ac->device) = NULL; - } - - return 0; -} -#endif - -/* -------------------------------------------------------------------------- - Driver Model - -------------------------------------------------------------------------- */ - -static void acpi_ac_notify(struct acpi_device *device, u32 event) -{ - struct acpi_ac *ac = acpi_driver_data(device); - - if (!ac) - return; + struct acpi_ac *ac = data; + struct acpi_device *adev = ac->device; switch (event) { default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - /* fall through */ + acpi_handle_debug(adev->handle, "Unsupported event [0x%x]\n", + event); + fallthrough; case ACPI_AC_NOTIFY_STATUS: case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: @@ -264,14 +141,12 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) msleep(ac_sleep_before_get_state_ms); acpi_ac_get_state(ac); - acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, + acpi_bus_generate_netlink_event(adev->pnp.device_class, + dev_name(&adev->dev), event, (u32) ac->state); - acpi_notifier_call_chain(device, event, (u32) ac->state); - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + acpi_notifier_call_chain(adev, event, (u32) ac->state); + power_supply_changed(ac->charger); } - - return; } static int acpi_ac_battery_notify(struct notifier_block *nb, @@ -283,7 +158,7 @@ static int acpi_ac_battery_notify(struct notifier_block *nb, /* * On HP Pavilion dv6-6179er AC status notifications aren't triggered * when adapter is plugged/unplugged. However, battery status - * notifcations are triggered when battery starts charging or + * notifications are triggered when battery starts charging or * discharging. Re-reading AC status triggers lost AC notifications, * if AC status has changed. */ @@ -300,95 +175,85 @@ static int __init thinkpad_e530_quirk(const struct dmi_system_id *d) return 0; } -static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d) +static int __init ac_only_quirk(const struct dmi_system_id *d) { - ac_check_pmic = 0; + ac_only = 1; return 0; } +/* Please keep this list alphabetically sorted */ static const struct dmi_system_id ac_dmi_table[] __initconst = { { - /* Thinkpad e530 */ - .callback = thinkpad_e530_quirk, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), - }, - }, - { - /* ECS EF20EA */ - .callback = ac_do_not_check_pmic_quirk, + /* Kodlix GK45 returning incorrect state */ + .callback = ac_only_quirk, .matches = { - DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), + DMI_MATCH(DMI_PRODUCT_NAME, "GK45"), }, }, { - /* Lenovo Ideapad Miix 320 */ - .callback = ac_do_not_check_pmic_quirk, + /* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */ + .callback = thinkpad_e530_quirk, .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"), - DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), }, }, {}, }; -static int acpi_ac_add(struct acpi_device *device) +static int acpi_ac_probe(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct power_supply_config psy_cfg = {}; - int result = 0; - struct acpi_ac *ac = NULL; - - - if (!device) - return -EINVAL; + struct acpi_ac *ac; + int result; ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL); if (!ac) return -ENOMEM; - ac->device = device; - strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_AC_CLASS); - device->driver_data = ac; + ac->device = adev; + strscpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME); + strscpy(acpi_device_class(adev), ACPI_AC_CLASS); + + platform_set_drvdata(pdev, ac); result = acpi_ac_get_state(ac); if (result) - goto end; + goto err_release_ac; psy_cfg.drv_data = ac; - ac->charger_desc.name = acpi_device_bid(device); -#ifdef CONFIG_ACPI_PROCFS_POWER - result = acpi_ac_add_fs(ac); - if (result) - goto end; -#endif + ac->charger_desc.name = acpi_device_bid(adev); ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS; ac->charger_desc.properties = ac_props; ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); ac->charger_desc.get_property = get_ac_property; - ac->charger = power_supply_register(&ac->device->dev, + ac->charger = power_supply_register(&pdev->dev, &ac->charger_desc, &psy_cfg); if (IS_ERR(ac->charger)) { result = PTR_ERR(ac->charger); - goto end; + goto err_release_ac; } - printk(KERN_INFO PREFIX "%s [%s] (%s)\n", - acpi_device_name(device), acpi_device_bid(device), - ac->state ? "on-line" : "off-line"); + pr_info("%s [%s] (%s-line)\n", acpi_device_name(adev), + acpi_device_bid(adev), str_on_off(ac->state)); ac->battery_nb.notifier_call = acpi_ac_battery_notify; register_acpi_notifier(&ac->battery_nb); -end: - if (result) { -#ifdef CONFIG_ACPI_PROCFS_POWER - acpi_ac_remove_fs(ac); -#endif - kfree(ac); - } + + result = acpi_dev_install_notify_handler(adev, ACPI_ALL_NOTIFY, + acpi_ac_notify, ac); + if (result) + goto err_unregister; + + return 0; + +err_unregister: + power_supply_unregister(ac->charger); + unregister_acpi_notifier(&ac->battery_nb); +err_release_ac: + kfree(ac); return result; } @@ -396,93 +261,65 @@ end: #ifdef CONFIG_PM_SLEEP static int acpi_ac_resume(struct device *dev) { - struct acpi_ac *ac; - unsigned old_state; - - if (!dev) - return -EINVAL; - - ac = acpi_driver_data(to_acpi_device(dev)); - if (!ac) - return -EINVAL; + struct acpi_ac *ac = dev_get_drvdata(dev); + unsigned int old_state; old_state = ac->state; if (acpi_ac_get_state(ac)) return 0; if (old_state != ac->state) - kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE); + power_supply_changed(ac->charger); + return 0; } #else #define acpi_ac_resume NULL #endif -static int acpi_ac_remove(struct acpi_device *device) +static void acpi_ac_remove(struct platform_device *pdev) { - struct acpi_ac *ac = NULL; - - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - ac = acpi_driver_data(device); + struct acpi_ac *ac = platform_get_drvdata(pdev); + acpi_dev_remove_notify_handler(ac->device, ACPI_ALL_NOTIFY, + acpi_ac_notify); power_supply_unregister(ac->charger); unregister_acpi_notifier(&ac->battery_nb); -#ifdef CONFIG_ACPI_PROCFS_POWER - acpi_ac_remove_fs(ac); -#endif - kfree(ac); - - return 0; } +static struct platform_driver acpi_ac_driver = { + .probe = acpi_ac_probe, + .remove = acpi_ac_remove, + .driver = { + .name = "ac", + .acpi_match_table = ac_device_ids, + .pm = &acpi_ac_pm, + }, +}; + static int __init acpi_ac_init(void) { - unsigned int i; int result; if (acpi_disabled) return -ENODEV; - dmi_check_system(ac_dmi_table); - - if (ac_check_pmic) { - for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++) - if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1", - acpi_ac_blacklist[i].hrv)) { - pr_info(PREFIX "AC: found native %s PMIC, not loading\n", - acpi_ac_blacklist[i].hid); - return -ENODEV; - } - } - -#ifdef CONFIG_ACPI_PROCFS_POWER - acpi_ac_dir = acpi_lock_ac_dir(); - if (!acpi_ac_dir) + if (acpi_quirk_skip_acpi_ac_and_battery()) return -ENODEV; -#endif + dmi_check_system(ac_dmi_table); - result = acpi_bus_register_driver(&acpi_ac_driver); - if (result < 0) { -#ifdef CONFIG_ACPI_PROCFS_POWER - acpi_unlock_ac_dir(acpi_ac_dir); -#endif + result = platform_driver_register(&acpi_ac_driver); + if (result < 0) return -ENODEV; - } return 0; } static void __exit acpi_ac_exit(void) { - acpi_bus_unregister_driver(&acpi_ac_driver); -#ifdef CONFIG_ACPI_PROCFS_POWER - acpi_unlock_ac_dir(acpi_ac_dir); -#endif + platform_driver_unregister(&acpi_ac_driver); } module_init(acpi_ac_init); module_exit(acpi_ac_exit); |
