LCOV - code coverage report
Current view: top level - drivers/acpi - ac.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 11 111 9.9 %
Date: 2022-04-01 14:17:54 Functions: 1 11 9.1 %
Branches: 6 58 10.3 %

           Branch data     Line data    Source code
       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
      36                 :            : ACPI_MODULE_NAME("ac");
      37                 :            : 
      38                 :            : MODULE_AUTHOR("Paul Diefenbaugh");
      39                 :            : MODULE_DESCRIPTION("ACPI AC Adapter Driver");
      40                 :            : MODULE_LICENSE("GPL");
      41                 :            : 
      42                 :            : 
      43                 :            : static int acpi_ac_add(struct acpi_device *device);
      44                 :            : static int acpi_ac_remove(struct acpi_device *device);
      45                 :            : static void acpi_ac_notify(struct acpi_device *device, u32 event);
      46                 :            : 
      47                 :            : struct acpi_ac_bl {
      48                 :            :         const char *hid;
      49                 :            :         int hrv;
      50                 :            : };
      51                 :            : 
      52                 :            : static const struct acpi_device_id ac_device_ids[] = {
      53                 :            :         {"ACPI0003", 0},
      54                 :            :         {"", 0},
      55                 :            : };
      56                 :            : MODULE_DEVICE_TABLE(acpi, ac_device_ids);
      57                 :            : 
      58                 :            : /* Lists of PMIC ACPI HIDs with an (often better) native charger driver */
      59                 :            : static 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
      65                 :            : static int acpi_ac_resume(struct device *dev);
      66                 :            : #endif
      67                 :            : static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
      68                 :            : 
      69                 :            : #ifdef CONFIG_ACPI_PROCFS_POWER
      70                 :            : extern struct proc_dir_entry *acpi_lock_ac_dir(void);
      71                 :            : extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
      72                 :            : #endif
      73                 :            : 
      74                 :            : 
      75                 :            : static int ac_sleep_before_get_state_ms;
      76                 :            : static int ac_check_pmic = 1;
      77                 :            : 
      78                 :            : static 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                 :            : 
      91                 :            : struct 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                 :            : 
     105                 :          0 : static int acpi_ac_get_state(struct acpi_ac *ac)
     106                 :            : {
     107                 :          0 :         acpi_status status = AE_OK;
     108                 :            : 
     109         [ #  # ]:          0 :         if (!ac)
     110                 :            :                 return -EINVAL;
     111                 :            : 
     112                 :          0 :         status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
     113                 :            :                                        &ac->state);
     114         [ #  # ]:          0 :         if (ACPI_FAILURE(status)) {
     115                 :          0 :                 ACPI_EXCEPTION((AE_INFO, status,
     116                 :            :                                 "Error reading AC Adapter state"));
     117                 :          0 :                 ac->state = ACPI_AC_STATUS_UNKNOWN;
     118                 :          0 :                 return -ENODEV;
     119                 :            :         }
     120                 :            : 
     121                 :            :         return 0;
     122                 :            : }
     123                 :            : 
     124                 :            : /* --------------------------------------------------------------------------
     125                 :            :                             sysfs I/F
     126                 :            :    -------------------------------------------------------------------------- */
     127                 :          0 : static int get_ac_property(struct power_supply *psy,
     128                 :            :                            enum power_supply_property psp,
     129                 :            :                            union power_supply_propval *val)
     130                 :            : {
     131                 :          0 :         struct acpi_ac *ac = to_acpi_ac(psy);
     132                 :            : 
     133         [ #  # ]:          0 :         if (!ac)
     134                 :            :                 return -ENODEV;
     135                 :            : 
     136         [ #  # ]:          0 :         if (acpi_ac_get_state(ac))
     137                 :            :                 return -ENODEV;
     138                 :            : 
     139         [ #  # ]:          0 :         switch (psp) {
     140                 :          0 :         case POWER_SUPPLY_PROP_ONLINE:
     141                 :          0 :                 val->intval = ac->state;
     142                 :          0 :                 break;
     143                 :            :         default:
     144                 :            :                 return -EINVAL;
     145                 :            :         }
     146                 :          0 :         return 0;
     147                 :            : }
     148                 :            : 
     149                 :            : static 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                 :            : 
     158                 :            : static struct proc_dir_entry *acpi_ac_dir;
     159                 :            : 
     160                 :            : static 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                 :            : 
     189                 :            : static 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                 :            : 
     210                 :            : static 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                 :            : 
     228                 :          0 : static void acpi_ac_notify(struct acpi_device *device, u32 event)
     229                 :            : {
     230         [ #  # ]:          0 :         struct acpi_ac *ac = acpi_driver_data(device);
     231                 :            : 
     232         [ #  # ]:          0 :         if (!ac)
     233                 :            :                 return;
     234                 :            : 
     235                 :          0 :         switch (event) {
     236                 :          0 :         default:
     237                 :            :                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
     238                 :          0 :                                   "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         [ #  # ]:          0 :                 if (ac_sleep_before_get_state_ms > 0)
     251                 :          0 :                         msleep(ac_sleep_before_get_state_ms);
     252                 :            : 
     253                 :          0 :                 acpi_ac_get_state(ac);
     254                 :          0 :                 acpi_bus_generate_netlink_event(device->pnp.device_class,
     255                 :            :                                                   dev_name(&device->dev), event,
     256         [ #  # ]:          0 :                                                   (u32) ac->state);
     257                 :          0 :                 acpi_notifier_call_chain(device, event, (u32) ac->state);
     258                 :          0 :                 kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
     259                 :            :         }
     260                 :            : 
     261                 :          0 :         return;
     262                 :            : }
     263                 :            : 
     264                 :          0 : static int acpi_ac_battery_notify(struct notifier_block *nb,
     265                 :            :                                   unsigned long action, void *data)
     266                 :            : {
     267                 :          0 :         struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb);
     268                 :          0 :         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         [ #  # ]:          0 :         if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 &&
     278         [ #  # ]:          0 :             event->type == ACPI_BATTERY_NOTIFY_STATUS)
     279                 :          0 :                 acpi_ac_get_state(ac);
     280                 :            : 
     281                 :          0 :         return NOTIFY_OK;
     282                 :            : }
     283                 :            : 
     284                 :          0 : static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
     285                 :            : {
     286                 :          0 :         ac_sleep_before_get_state_ms = 1000;
     287                 :          0 :         return 0;
     288                 :            : }
     289                 :            : 
     290                 :          0 : static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d)
     291                 :            : {
     292                 :          0 :         ac_check_pmic = 0;
     293                 :          0 :         return 0;
     294                 :            : }
     295                 :            : 
     296                 :            : static 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                 :            : 
     324                 :          0 : static int acpi_ac_add(struct acpi_device *device)
     325                 :            : {
     326                 :          0 :         struct power_supply_config psy_cfg = {};
     327                 :          0 :         int result = 0;
     328                 :          0 :         struct acpi_ac *ac = NULL;
     329                 :            : 
     330                 :            : 
     331         [ #  # ]:          0 :         if (!device)
     332                 :            :                 return -EINVAL;
     333                 :            : 
     334                 :          0 :         ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
     335         [ #  # ]:          0 :         if (!ac)
     336                 :            :                 return -ENOMEM;
     337                 :            : 
     338                 :          0 :         ac->device = device;
     339                 :          0 :         strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
     340                 :          0 :         strcpy(acpi_device_class(device), ACPI_AC_CLASS);
     341                 :          0 :         device->driver_data = ac;
     342                 :            : 
     343                 :          0 :         result = acpi_ac_get_state(ac);
     344         [ #  # ]:          0 :         if (result)
     345                 :          0 :                 goto end;
     346                 :            : 
     347                 :          0 :         psy_cfg.drv_data = ac;
     348                 :            : 
     349                 :          0 :         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                 :          0 :         ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
     356                 :          0 :         ac->charger_desc.properties = ac_props;
     357                 :          0 :         ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
     358                 :          0 :         ac->charger_desc.get_property = get_ac_property;
     359                 :          0 :         ac->charger = power_supply_register(&ac->device->dev,
     360                 :          0 :                                             &ac->charger_desc, &psy_cfg);
     361         [ #  # ]:          0 :         if (IS_ERR(ac->charger)) {
     362                 :          0 :                 result = PTR_ERR(ac->charger);
     363                 :          0 :                 goto end;
     364                 :            :         }
     365                 :            : 
     366                 :          0 :         printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
     367                 :            :                acpi_device_name(device), acpi_device_bid(device),
     368         [ #  # ]:          0 :                ac->state ? "on-line" : "off-line");
     369                 :            : 
     370                 :          0 :         ac->battery_nb.notifier_call = acpi_ac_battery_notify;
     371                 :          0 :         register_acpi_notifier(&ac->battery_nb);
     372                 :          0 : end:
     373         [ #  # ]:          0 :         if (result) {
     374                 :            : #ifdef CONFIG_ACPI_PROCFS_POWER
     375                 :            :                 acpi_ac_remove_fs(ac);
     376                 :            : #endif
     377                 :          0 :                 kfree(ac);
     378                 :            :         }
     379                 :            : 
     380                 :            :         return result;
     381                 :            : }
     382                 :            : 
     383                 :            : #ifdef CONFIG_PM_SLEEP
     384                 :          0 : static int acpi_ac_resume(struct device *dev)
     385                 :            : {
     386                 :          0 :         struct acpi_ac *ac;
     387                 :          0 :         unsigned old_state;
     388                 :            : 
     389         [ #  # ]:          0 :         if (!dev)
     390                 :            :                 return -EINVAL;
     391                 :            : 
     392         [ #  # ]:          0 :         ac = acpi_driver_data(to_acpi_device(dev));
     393         [ #  # ]:          0 :         if (!ac)
     394                 :            :                 return -EINVAL;
     395                 :            : 
     396                 :          0 :         old_state = ac->state;
     397         [ #  # ]:          0 :         if (acpi_ac_get_state(ac))
     398                 :            :                 return 0;
     399         [ #  # ]:          0 :         if (old_state != ac->state)
     400                 :          0 :                 kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
     401                 :            :         return 0;
     402                 :            : }
     403                 :            : #else
     404                 :            : #define acpi_ac_resume NULL
     405                 :            : #endif
     406                 :            : 
     407                 :          0 : static int acpi_ac_remove(struct acpi_device *device)
     408                 :            : {
     409                 :          0 :         struct acpi_ac *ac = NULL;
     410                 :            : 
     411                 :            : 
     412   [ #  #  #  # ]:          0 :         if (!device || !acpi_driver_data(device))
     413                 :            :                 return -EINVAL;
     414                 :            : 
     415                 :          0 :         ac = acpi_driver_data(device);
     416                 :            : 
     417                 :          0 :         power_supply_unregister(ac->charger);
     418                 :          0 :         unregister_acpi_notifier(&ac->battery_nb);
     419                 :            : 
     420                 :            : #ifdef CONFIG_ACPI_PROCFS_POWER
     421                 :            :         acpi_ac_remove_fs(ac);
     422                 :            : #endif
     423                 :            : 
     424                 :          0 :         kfree(ac);
     425                 :            : 
     426                 :          0 :         return 0;
     427                 :            : }
     428                 :            : 
     429                 :         11 : static int __init acpi_ac_init(void)
     430                 :            : {
     431                 :         11 :         unsigned int i;
     432                 :         11 :         int result;
     433                 :            : 
     434         [ +  - ]:         11 :         if (acpi_disabled)
     435                 :            :                 return -ENODEV;
     436                 :            : 
     437                 :         11 :         dmi_check_system(ac_dmi_table);
     438                 :            : 
     439         [ +  - ]:         11 :         if (ac_check_pmic) {
     440         [ +  + ]:         33 :                 for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
     441         [ -  + ]:         22 :                         if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
     442                 :         22 :                                              acpi_ac_blacklist[i].hrv)) {
     443                 :          0 :                                 pr_info(PREFIX "AC: found native %s PMIC, not loading\n",
     444                 :            :                                         acpi_ac_blacklist[i].hid);
     445                 :          0 :                                 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                 :         11 :         result = acpi_bus_register_driver(&acpi_ac_driver);
     457         [ -  + ]:         11 :         if (result < 0) {
     458                 :            : #ifdef CONFIG_ACPI_PROCFS_POWER
     459                 :            :                 acpi_unlock_ac_dir(acpi_ac_dir);
     460                 :            : #endif
     461                 :          0 :                 return -ENODEV;
     462                 :            :         }
     463                 :            : 
     464                 :            :         return 0;
     465                 :            : }
     466                 :            : 
     467                 :          0 : static void __exit acpi_ac_exit(void)
     468                 :            : {
     469                 :          0 :         acpi_bus_unregister_driver(&acpi_ac_driver);
     470                 :            : #ifdef CONFIG_ACPI_PROCFS_POWER
     471                 :            :         acpi_unlock_ac_dir(acpi_ac_dir);
     472                 :            : #endif
     473                 :          0 : }
     474                 :            : module_init(acpi_ac_init);
     475                 :            : module_exit(acpi_ac_exit);

Generated by: LCOV version 1.14