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#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/slab.h>
  12#include <linux/init.h>
  13#include <linux/types.h>
  14#include <linux/dmi.h>
  15#include <linux/delay.h>
  16#ifdef CONFIG_ACPI_PROCFS_POWER
  17#include <linux/proc_fs.h>
  18#include <linux/seq_file.h>
  19#endif
  20#include <linux/platform_device.h>
  21#include <linux/power_supply.h>
  22#include <linux/acpi.h>
  23#include <acpi/battery.h>
  24
  25#define PREFIX "ACPI: "
  26
  27#define ACPI_AC_CLASS                   "ac_adapter"
  28#define ACPI_AC_DEVICE_NAME             "AC Adapter"
  29#define ACPI_AC_FILE_STATE              "state"
  30#define ACPI_AC_NOTIFY_STATUS           0x80
  31#define ACPI_AC_STATUS_OFFLINE          0x00
  32#define ACPI_AC_STATUS_ONLINE           0x01
  33#define ACPI_AC_STATUS_UNKNOWN          0xFF
  34
  35#define _COMPONENT              ACPI_AC_COMPONENT
  36ACPI_MODULE_NAME("ac");
  37
  38MODULE_AUTHOR("Paul Diefenbaugh");
  39MODULE_DESCRIPTION("ACPI AC Adapter Driver");
  40MODULE_LICENSE("GPL");
  41
  42
  43static int acpi_ac_add(struct acpi_device *device);
  44static int acpi_ac_remove(struct acpi_device *device);
  45static void acpi_ac_notify(struct acpi_device *device, u32 event);
  46
  47struct acpi_ac_bl {
  48        const char *hid;
  49        int hrv;
  50};
  51
  52static const struct acpi_device_id ac_device_ids[] = {
  53        {"ACPI0003", 0},
  54        {"", 0},
  55};
  56MODULE_DEVICE_TABLE(acpi, ac_device_ids);
  57
  58/* Lists of PMIC ACPI HIDs with an (often better) native charger driver */
  59static const struct acpi_ac_bl acpi_ac_blacklist[] = {
  60        { "INT33F4", -1 }, /* X-Powers AXP288 PMIC */
  61        { "INT34D3",  3 }, /* Intel Cherrytrail Whiskey Cove PMIC */
  62};
  63
  64#ifdef CONFIG_PM_SLEEP
  65static int acpi_ac_resume(struct device *dev);
  66#endif
  67static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
  68
  69#ifdef CONFIG_ACPI_PROCFS_POWER
  70extern struct proc_dir_entry *acpi_lock_ac_dir(void);
  71extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
  72#endif
  73
  74
  75static int ac_sleep_before_get_state_ms;
  76static int ac_check_pmic = 1;
  77
  78static struct acpi_driver acpi_ac_driver = {
  79        .name = "ac",
  80        .class = ACPI_AC_CLASS,
  81        .ids = ac_device_ids,
  82        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
  83        .ops = {
  84                .add = acpi_ac_add,
  85                .remove = acpi_ac_remove,
  86                .notify = acpi_ac_notify,
  87                },
  88        .drv.pm = &acpi_ac_pm,
  89};
  90
  91struct acpi_ac {
  92        struct power_supply *charger;
  93        struct power_supply_desc charger_desc;
  94        struct acpi_device * device;
  95        unsigned long long state;
  96        struct notifier_block battery_nb;
  97};
  98
  99#define to_acpi_ac(x) power_supply_get_drvdata(x)
 100
 101/* --------------------------------------------------------------------------
 102                               AC Adapter Management
 103   -------------------------------------------------------------------------- */
 104
 105static int acpi_ac_get_state(struct acpi_ac *ac)
 106{
 107        acpi_status status = AE_OK;
 108
 109        if (!ac)
 110                return -EINVAL;
 111
 112        status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
 113                                       &ac->state);
 114        if (ACPI_FAILURE(status)) {
 115                ACPI_EXCEPTION((AE_INFO, status,
 116                                "Error reading AC Adapter state"));
 117                ac->state = ACPI_AC_STATUS_UNKNOWN;
 118                return -ENODEV;
 119        }
 120
 121        return 0;
 122}
 123
 124/* --------------------------------------------------------------------------
 125                            sysfs I/F
 126   -------------------------------------------------------------------------- */
 127static int get_ac_property(struct power_supply *psy,
 128                           enum power_supply_property psp,
 129                           union power_supply_propval *val)
 130{
 131        struct acpi_ac *ac = to_acpi_ac(psy);
 132
 133        if (!ac)
 134                return -ENODEV;
 135
 136        if (acpi_ac_get_state(ac))
 137                return -ENODEV;
 138
 139        switch (psp) {
 140        case POWER_SUPPLY_PROP_ONLINE:
 141                val->intval = ac->state;
 142                break;
 143        default:
 144                return -EINVAL;
 145        }
 146        return 0;
 147}
 148
 149static enum power_supply_property ac_props[] = {
 150        POWER_SUPPLY_PROP_ONLINE,
 151};
 152
 153#ifdef CONFIG_ACPI_PROCFS_POWER
 154/* --------------------------------------------------------------------------
 155                              FS Interface (/proc)
 156   -------------------------------------------------------------------------- */
 157
 158static struct proc_dir_entry *acpi_ac_dir;
 159
 160static int acpi_ac_seq_show(struct seq_file *seq, void *offset)
 161{
 162        struct acpi_ac *ac = seq->private;
 163
 164
 165        if (!ac)
 166                return 0;
 167
 168        if (acpi_ac_get_state(ac)) {
 169                seq_puts(seq, "ERROR: Unable to read AC Adapter state\n");
 170                return 0;
 171        }
 172
 173        seq_puts(seq, "state:                   ");
 174        switch (ac->state) {
 175        case ACPI_AC_STATUS_OFFLINE:
 176                seq_puts(seq, "off-line\n");
 177                break;
 178        case ACPI_AC_STATUS_ONLINE:
 179                seq_puts(seq, "on-line\n");
 180                break;
 181        default:
 182                seq_puts(seq, "unknown\n");
 183                break;
 184        }
 185
 186        return 0;
 187}
 188
 189static int acpi_ac_add_fs(struct acpi_ac *ac)
 190{
 191        struct proc_dir_entry *entry = NULL;
 192
 193        printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded,"
 194                        " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
 195        if (!acpi_device_dir(ac->device)) {
 196                acpi_device_dir(ac->device) =
 197                        proc_mkdir(acpi_device_bid(ac->device), acpi_ac_dir);
 198                if (!acpi_device_dir(ac->device))
 199                        return -ENODEV;
 200        }
 201
 202        /* 'state' [R] */
 203        entry = proc_create_single_data(ACPI_AC_FILE_STATE, S_IRUGO,
 204                        acpi_device_dir(ac->device), acpi_ac_seq_show, ac);
 205        if (!entry)
 206                return -ENODEV;
 207        return 0;
 208}
 209
 210static int acpi_ac_remove_fs(struct acpi_ac *ac)
 211{
 212
 213        if (acpi_device_dir(ac->device)) {
 214                remove_proc_entry(ACPI_AC_FILE_STATE,
 215                                  acpi_device_dir(ac->device));
 216                remove_proc_entry(acpi_device_bid(ac->device), acpi_ac_dir);
 217                acpi_device_dir(ac->device) = NULL;
 218        }
 219
 220        return 0;
 221}
 222#endif
 223
 224/* --------------------------------------------------------------------------
 225                                   Driver Model
 226   -------------------------------------------------------------------------- */
 227
 228static void acpi_ac_notify(struct acpi_device *device, u32 event)
 229{
 230        struct acpi_ac *ac = acpi_driver_data(device);
 231
 232        if (!ac)
 233                return;
 234
 235        switch (event) {
 236        default:
 237                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 238                                  "Unsupported event [0x%x]\n", event));
 239        /* fall through */
 240        case ACPI_AC_NOTIFY_STATUS:
 241        case ACPI_NOTIFY_BUS_CHECK:
 242        case ACPI_NOTIFY_DEVICE_CHECK:
 243                /*
 244                 * A buggy BIOS may notify AC first and then sleep for
 245                 * a specific time before doing actual operations in the
 246                 * EC event handler (_Qxx). This will cause the AC state
 247                 * reported by the ACPI event to be incorrect, so wait for a
 248                 * specific time for the EC event handler to make progress.
 249                 */
 250                if (ac_sleep_before_get_state_ms > 0)
 251                        msleep(ac_sleep_before_get_state_ms);
 252
 253                acpi_ac_get_state(ac);
 254                acpi_bus_generate_netlink_event(device->pnp.device_class,
 255                                                  dev_name(&device->dev), event,
 256                                                  (u32) ac->state);
 257                acpi_notifier_call_chain(device, event, (u32) ac->state);
 258                kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
 259        }
 260
 261        return;
 262}
 263
 264static int acpi_ac_battery_notify(struct notifier_block *nb,
 265                                  unsigned long action, void *data)
 266{
 267        struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb);
 268        struct acpi_bus_event *event = (struct acpi_bus_event *)data;
 269
 270        /*
 271         * On HP Pavilion dv6-6179er AC status notifications aren't triggered
 272         * when adapter is plugged/unplugged. However, battery status
 273         * notifcations are triggered when battery starts charging or
 274         * discharging. Re-reading AC status triggers lost AC notifications,
 275         * if AC status has changed.
 276         */
 277        if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 &&
 278            event->type == ACPI_BATTERY_NOTIFY_STATUS)
 279                acpi_ac_get_state(ac);
 280
 281        return NOTIFY_OK;
 282}
 283
 284static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
 285{
 286        ac_sleep_before_get_state_ms = 1000;
 287        return 0;
 288}
 289
 290static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d)
 291{
 292        ac_check_pmic = 0;
 293        return 0;
 294}
 295
 296static const struct dmi_system_id ac_dmi_table[]  __initconst = {
 297        {
 298        /* Thinkpad e530 */
 299        .callback = thinkpad_e530_quirk,
 300        .matches = {
 301                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 302                DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
 303                },
 304        },
 305        {
 306                /* ECS EF20EA */
 307                .callback = ac_do_not_check_pmic_quirk,
 308                .matches = {
 309                        DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
 310                },
 311        },
 312        {
 313                /* Lenovo Ideapad Miix 320 */
 314                .callback = ac_do_not_check_pmic_quirk,
 315                .matches = {
 316                  DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
 317                  DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
 318                  DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
 319                },
 320        },
 321        {},
 322};
 323
 324static int acpi_ac_add(struct acpi_device *device)
 325{
 326        struct power_supply_config psy_cfg = {};
 327        int result = 0;
 328        struct acpi_ac *ac = NULL;
 329
 330
 331        if (!device)
 332                return -EINVAL;
 333
 334        ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
 335        if (!ac)
 336                return -ENOMEM;
 337
 338        ac->device = device;
 339        strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
 340        strcpy(acpi_device_class(device), ACPI_AC_CLASS);
 341        device->driver_data = ac;
 342
 343        result = acpi_ac_get_state(ac);
 344        if (result)
 345                goto end;
 346
 347        psy_cfg.drv_data = ac;
 348
 349        ac->charger_desc.name = acpi_device_bid(device);
 350#ifdef CONFIG_ACPI_PROCFS_POWER
 351        result = acpi_ac_add_fs(ac);
 352        if (result)
 353                goto end;
 354#endif
 355        ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
 356        ac->charger_desc.properties = ac_props;
 357        ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
 358        ac->charger_desc.get_property = get_ac_property;
 359        ac->charger = power_supply_register(&ac->device->dev,
 360                                            &ac->charger_desc, &psy_cfg);
 361        if (IS_ERR(ac->charger)) {
 362                result = PTR_ERR(ac->charger);
 363                goto end;
 364        }
 365
 366        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 367               acpi_device_name(device), acpi_device_bid(device),
 368               ac->state ? "on-line" : "off-line");
 369
 370        ac->battery_nb.notifier_call = acpi_ac_battery_notify;
 371        register_acpi_notifier(&ac->battery_nb);
 372end:
 373        if (result) {
 374#ifdef CONFIG_ACPI_PROCFS_POWER
 375                acpi_ac_remove_fs(ac);
 376#endif
 377                kfree(ac);
 378        }
 379
 380        return result;
 381}
 382
 383#ifdef CONFIG_PM_SLEEP
 384static int acpi_ac_resume(struct device *dev)
 385{
 386        struct acpi_ac *ac;
 387        unsigned old_state;
 388
 389        if (!dev)
 390                return -EINVAL;
 391
 392        ac = acpi_driver_data(to_acpi_device(dev));
 393        if (!ac)
 394                return -EINVAL;
 395
 396        old_state = ac->state;
 397        if (acpi_ac_get_state(ac))
 398                return 0;
 399        if (old_state != ac->state)
 400                kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
 401        return 0;
 402}
 403#else
 404#define acpi_ac_resume NULL
 405#endif
 406
 407static int acpi_ac_remove(struct acpi_device *device)
 408{
 409        struct acpi_ac *ac = NULL;
 410
 411
 412        if (!device || !acpi_driver_data(device))
 413                return -EINVAL;
 414
 415        ac = acpi_driver_data(device);
 416
 417        power_supply_unregister(ac->charger);
 418        unregister_acpi_notifier(&ac->battery_nb);
 419
 420#ifdef CONFIG_ACPI_PROCFS_POWER
 421        acpi_ac_remove_fs(ac);
 422#endif
 423
 424        kfree(ac);
 425
 426        return 0;
 427}
 428
 429static int __init acpi_ac_init(void)
 430{
 431        unsigned int i;
 432        int result;
 433
 434        if (acpi_disabled)
 435                return -ENODEV;
 436
 437        dmi_check_system(ac_dmi_table);
 438
 439        if (ac_check_pmic) {
 440                for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
 441                        if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
 442                                             acpi_ac_blacklist[i].hrv)) {
 443                                pr_info(PREFIX "AC: found native %s PMIC, not loading\n",
 444                                        acpi_ac_blacklist[i].hid);
 445                                return -ENODEV;
 446                        }
 447        }
 448
 449#ifdef CONFIG_ACPI_PROCFS_POWER
 450        acpi_ac_dir = acpi_lock_ac_dir();
 451        if (!acpi_ac_dir)
 452                return -ENODEV;
 453#endif
 454
 455
 456        result = acpi_bus_register_driver(&acpi_ac_driver);
 457        if (result < 0) {
 458#ifdef CONFIG_ACPI_PROCFS_POWER
 459                acpi_unlock_ac_dir(acpi_ac_dir);
 460#endif
 461                return -ENODEV;
 462        }
 463
 464        return 0;
 465}
 466
 467static void __exit acpi_ac_exit(void)
 468{
 469        acpi_bus_unregister_driver(&acpi_ac_driver);
 470#ifdef CONFIG_ACPI_PROCFS_POWER
 471        acpi_unlock_ac_dir(acpi_ac_dir);
 472#endif
 473}
 474module_init(acpi_ac_init);
 475module_exit(acpi_ac_exit);
 476