linux/drivers/acpi/ac.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  acpi_ac.c - ACPI AC Adapter Driver (Revision: 27)
   4 *
   5 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   6 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   7 */
   8
   9#define pr_fmt(fmt) "ACPI: AC: " fmt
  10
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/init.h>
  15#include <linux/types.h>
  16#include <linux/dmi.h>
  17#include <linux/delay.h>
  18#include <linux/platform_device.h>
  19#include <linux/power_supply.h>
  20#include <linux/acpi.h>
  21#include <acpi/battery.h>
  22
  23#define ACPI_AC_CLASS                   "ac_adapter"
  24#define ACPI_AC_DEVICE_NAME             "AC Adapter"
  25#define ACPI_AC_FILE_STATE              "state"
  26#define ACPI_AC_NOTIFY_STATUS           0x80
  27#define ACPI_AC_STATUS_OFFLINE          0x00
  28#define ACPI_AC_STATUS_ONLINE           0x01
  29#define ACPI_AC_STATUS_UNKNOWN          0xFF
  30
  31MODULE_AUTHOR("Paul Diefenbaugh");
  32MODULE_DESCRIPTION("ACPI AC Adapter Driver");
  33MODULE_LICENSE("GPL");
  34
  35
  36static int acpi_ac_add(struct acpi_device *device);
  37static int acpi_ac_remove(struct acpi_device *device);
  38static void acpi_ac_notify(struct acpi_device *device, u32 event);
  39
  40struct acpi_ac_bl {
  41        const char *hid;
  42        int hrv;
  43};
  44
  45static const struct acpi_device_id ac_device_ids[] = {
  46        {"ACPI0003", 0},
  47        {"", 0},
  48};
  49MODULE_DEVICE_TABLE(acpi, ac_device_ids);
  50
  51/* Lists of PMIC ACPI HIDs with an (often better) native charger driver */
  52static const struct acpi_ac_bl acpi_ac_blacklist[] = {
  53        { "INT33F4", -1 }, /* X-Powers AXP288 PMIC */
  54        { "INT34D3",  3 }, /* Intel Cherrytrail Whiskey Cove PMIC */
  55};
  56
  57#ifdef CONFIG_PM_SLEEP
  58static int acpi_ac_resume(struct device *dev);
  59#endif
  60static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
  61
  62static int ac_sleep_before_get_state_ms;
  63static int ac_check_pmic = 1;
  64
  65static struct acpi_driver acpi_ac_driver = {
  66        .name = "ac",
  67        .class = ACPI_AC_CLASS,
  68        .ids = ac_device_ids,
  69        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
  70        .ops = {
  71                .add = acpi_ac_add,
  72                .remove = acpi_ac_remove,
  73                .notify = acpi_ac_notify,
  74                },
  75        .drv.pm = &acpi_ac_pm,
  76};
  77
  78struct acpi_ac {
  79        struct power_supply *charger;
  80        struct power_supply_desc charger_desc;
  81        struct acpi_device *device;
  82        unsigned long long state;
  83        struct notifier_block battery_nb;
  84};
  85
  86#define to_acpi_ac(x) power_supply_get_drvdata(x)
  87
  88/* AC Adapter Management */
  89static int acpi_ac_get_state(struct acpi_ac *ac)
  90{
  91        acpi_status status = AE_OK;
  92
  93        if (!ac)
  94                return -EINVAL;
  95
  96        status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
  97                                       &ac->state);
  98        if (ACPI_FAILURE(status)) {
  99                acpi_handle_info(ac->device->handle,
 100                                "Error reading AC Adapter state: %s\n",
 101                                acpi_format_exception(status));
 102                ac->state = ACPI_AC_STATUS_UNKNOWN;
 103                return -ENODEV;
 104        }
 105
 106        return 0;
 107}
 108
 109/* sysfs I/F */
 110static int get_ac_property(struct power_supply *psy,
 111                           enum power_supply_property psp,
 112                           union power_supply_propval *val)
 113{
 114        struct acpi_ac *ac = to_acpi_ac(psy);
 115
 116        if (!ac)
 117                return -ENODEV;
 118
 119        if (acpi_ac_get_state(ac))
 120                return -ENODEV;
 121
 122        switch (psp) {
 123        case POWER_SUPPLY_PROP_ONLINE:
 124                val->intval = ac->state;
 125                break;
 126        default:
 127                return -EINVAL;
 128        }
 129        return 0;
 130}
 131
 132static enum power_supply_property ac_props[] = {
 133        POWER_SUPPLY_PROP_ONLINE,
 134};
 135
 136/* Driver Model */
 137static void acpi_ac_notify(struct acpi_device *device, u32 event)
 138{
 139        struct acpi_ac *ac = acpi_driver_data(device);
 140
 141        if (!ac)
 142                return;
 143
 144        switch (event) {
 145        default:
 146                acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
 147                                  event);
 148                fallthrough;
 149        case ACPI_AC_NOTIFY_STATUS:
 150        case ACPI_NOTIFY_BUS_CHECK:
 151        case ACPI_NOTIFY_DEVICE_CHECK:
 152                /*
 153                 * A buggy BIOS may notify AC first and then sleep for
 154                 * a specific time before doing actual operations in the
 155                 * EC event handler (_Qxx). This will cause the AC state
 156                 * reported by the ACPI event to be incorrect, so wait for a
 157                 * specific time for the EC event handler to make progress.
 158                 */
 159                if (ac_sleep_before_get_state_ms > 0)
 160                        msleep(ac_sleep_before_get_state_ms);
 161
 162                acpi_ac_get_state(ac);
 163                acpi_bus_generate_netlink_event(device->pnp.device_class,
 164                                                  dev_name(&device->dev), event,
 165                                                  (u32) ac->state);
 166                acpi_notifier_call_chain(device, event, (u32) ac->state);
 167                kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
 168        }
 169}
 170
 171static int acpi_ac_battery_notify(struct notifier_block *nb,
 172                                  unsigned long action, void *data)
 173{
 174        struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb);
 175        struct acpi_bus_event *event = (struct acpi_bus_event *)data;
 176
 177        /*
 178         * On HP Pavilion dv6-6179er AC status notifications aren't triggered
 179         * when adapter is plugged/unplugged. However, battery status
 180         * notifications are triggered when battery starts charging or
 181         * discharging. Re-reading AC status triggers lost AC notifications,
 182         * if AC status has changed.
 183         */
 184        if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 &&
 185            event->type == ACPI_BATTERY_NOTIFY_STATUS)
 186                acpi_ac_get_state(ac);
 187
 188        return NOTIFY_OK;
 189}
 190
 191static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
 192{
 193        ac_sleep_before_get_state_ms = 1000;
 194        return 0;
 195}
 196
 197static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d)
 198{
 199        ac_check_pmic = 0;
 200        return 0;
 201}
 202
 203/* Please keep this list alphabetically sorted */
 204static const struct dmi_system_id ac_dmi_table[]  __initconst = {
 205        {
 206                /* ECS EF20EA, AXP288 PMIC but uses separate fuel-gauge */
 207                .callback = ac_do_not_check_pmic_quirk,
 208                .matches = {
 209                        DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
 210                },
 211        },
 212        {
 213                /* Lenovo Ideapad Miix 320, AXP288 PMIC, separate fuel-gauge */
 214                .callback = ac_do_not_check_pmic_quirk,
 215                .matches = {
 216                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 217                        DMI_MATCH(DMI_PRODUCT_NAME, "80XF"),
 218                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
 219                },
 220        },
 221        {
 222                /* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */
 223                .callback = thinkpad_e530_quirk,
 224                .matches = {
 225                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 226                        DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
 227                },
 228        },
 229        {},
 230};
 231
 232static int acpi_ac_add(struct acpi_device *device)
 233{
 234        struct power_supply_config psy_cfg = {};
 235        int result = 0;
 236        struct acpi_ac *ac = NULL;
 237
 238
 239        if (!device)
 240                return -EINVAL;
 241
 242        ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
 243        if (!ac)
 244                return -ENOMEM;
 245
 246        ac->device = device;
 247        strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
 248        strcpy(acpi_device_class(device), ACPI_AC_CLASS);
 249        device->driver_data = ac;
 250
 251        result = acpi_ac_get_state(ac);
 252        if (result)
 253                goto end;
 254
 255        psy_cfg.drv_data = ac;
 256
 257        ac->charger_desc.name = acpi_device_bid(device);
 258        ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
 259        ac->charger_desc.properties = ac_props;
 260        ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
 261        ac->charger_desc.get_property = get_ac_property;
 262        ac->charger = power_supply_register(&ac->device->dev,
 263                                            &ac->charger_desc, &psy_cfg);
 264        if (IS_ERR(ac->charger)) {
 265                result = PTR_ERR(ac->charger);
 266                goto end;
 267        }
 268
 269        pr_info("%s [%s] (%s)\n", acpi_device_name(device),
 270                acpi_device_bid(device), ac->state ? "on-line" : "off-line");
 271
 272        ac->battery_nb.notifier_call = acpi_ac_battery_notify;
 273        register_acpi_notifier(&ac->battery_nb);
 274end:
 275        if (result)
 276                kfree(ac);
 277
 278        return result;
 279}
 280
 281#ifdef CONFIG_PM_SLEEP
 282static int acpi_ac_resume(struct device *dev)
 283{
 284        struct acpi_ac *ac;
 285        unsigned int old_state;
 286
 287        if (!dev)
 288                return -EINVAL;
 289
 290        ac = acpi_driver_data(to_acpi_device(dev));
 291        if (!ac)
 292                return -EINVAL;
 293
 294        old_state = ac->state;
 295        if (acpi_ac_get_state(ac))
 296                return 0;
 297        if (old_state != ac->state)
 298                kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
 299        return 0;
 300}
 301#else
 302#define acpi_ac_resume NULL
 303#endif
 304
 305static int acpi_ac_remove(struct acpi_device *device)
 306{
 307        struct acpi_ac *ac = NULL;
 308
 309
 310        if (!device || !acpi_driver_data(device))
 311                return -EINVAL;
 312
 313        ac = acpi_driver_data(device);
 314
 315        power_supply_unregister(ac->charger);
 316        unregister_acpi_notifier(&ac->battery_nb);
 317
 318        kfree(ac);
 319
 320        return 0;
 321}
 322
 323static int __init acpi_ac_init(void)
 324{
 325        unsigned int i;
 326        int result;
 327
 328        if (acpi_disabled)
 329                return -ENODEV;
 330
 331        dmi_check_system(ac_dmi_table);
 332
 333        if (ac_check_pmic) {
 334                for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
 335                        if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
 336                                             acpi_ac_blacklist[i].hrv)) {
 337                                pr_info("found native %s PMIC, not loading\n",
 338                                        acpi_ac_blacklist[i].hid);
 339                                return -ENODEV;
 340                        }
 341        }
 342
 343        result = acpi_bus_register_driver(&acpi_ac_driver);
 344        if (result < 0)
 345                return -ENODEV;
 346
 347        return 0;
 348}
 349
 350static void __exit acpi_ac_exit(void)
 351{
 352        acpi_bus_unregister_driver(&acpi_ac_driver);
 353}
 354module_init(acpi_ac_init);
 355module_exit(acpi_ac_exit);
 356