| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $) |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
|---|
| 5 | 6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or (at |
|---|
| 12 | | - * your option) any later version. |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 15 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 17 | | - * General Public License for more details. |
|---|
| 18 | | - * |
|---|
| 19 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 20 | 7 | */ |
|---|
| 21 | 8 | |
|---|
| 22 | 9 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 26 | 13 | #include <linux/types.h> |
|---|
| 27 | 14 | #include <linux/dmi.h> |
|---|
| 28 | 15 | #include <linux/delay.h> |
|---|
| 29 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 30 | | -#include <linux/proc_fs.h> |
|---|
| 31 | | -#include <linux/seq_file.h> |
|---|
| 32 | | -#endif |
|---|
| 33 | 16 | #include <linux/platform_device.h> |
|---|
| 34 | 17 | #include <linux/power_supply.h> |
|---|
| 35 | 18 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 79 | 62 | #endif |
|---|
| 80 | 63 | static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); |
|---|
| 81 | 64 | |
|---|
| 82 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 83 | | -extern struct proc_dir_entry *acpi_lock_ac_dir(void); |
|---|
| 84 | | -extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); |
|---|
| 85 | | -#endif |
|---|
| 86 | | - |
|---|
| 87 | | - |
|---|
| 88 | 65 | static int ac_sleep_before_get_state_ms; |
|---|
| 89 | 66 | static int ac_check_pmic = 1; |
|---|
| 67 | +static int ac_only; |
|---|
| 90 | 68 | |
|---|
| 91 | 69 | static struct acpi_driver acpi_ac_driver = { |
|---|
| 92 | 70 | .name = "ac", |
|---|
| .. | .. |
|---|
| 121 | 99 | |
|---|
| 122 | 100 | if (!ac) |
|---|
| 123 | 101 | return -EINVAL; |
|---|
| 102 | + |
|---|
| 103 | + if (ac_only) { |
|---|
| 104 | + ac->state = 1; |
|---|
| 105 | + return 0; |
|---|
| 106 | + } |
|---|
| 124 | 107 | |
|---|
| 125 | 108 | status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL, |
|---|
| 126 | 109 | &ac->state); |
|---|
| .. | .. |
|---|
| 163 | 146 | POWER_SUPPLY_PROP_ONLINE, |
|---|
| 164 | 147 | }; |
|---|
| 165 | 148 | |
|---|
| 166 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 167 | | -/* -------------------------------------------------------------------------- |
|---|
| 168 | | - FS Interface (/proc) |
|---|
| 169 | | - -------------------------------------------------------------------------- */ |
|---|
| 170 | | - |
|---|
| 171 | | -static struct proc_dir_entry *acpi_ac_dir; |
|---|
| 172 | | - |
|---|
| 173 | | -static int acpi_ac_seq_show(struct seq_file *seq, void *offset) |
|---|
| 174 | | -{ |
|---|
| 175 | | - struct acpi_ac *ac = seq->private; |
|---|
| 176 | | - |
|---|
| 177 | | - |
|---|
| 178 | | - if (!ac) |
|---|
| 179 | | - return 0; |
|---|
| 180 | | - |
|---|
| 181 | | - if (acpi_ac_get_state(ac)) { |
|---|
| 182 | | - seq_puts(seq, "ERROR: Unable to read AC Adapter state\n"); |
|---|
| 183 | | - return 0; |
|---|
| 184 | | - } |
|---|
| 185 | | - |
|---|
| 186 | | - seq_puts(seq, "state: "); |
|---|
| 187 | | - switch (ac->state) { |
|---|
| 188 | | - case ACPI_AC_STATUS_OFFLINE: |
|---|
| 189 | | - seq_puts(seq, "off-line\n"); |
|---|
| 190 | | - break; |
|---|
| 191 | | - case ACPI_AC_STATUS_ONLINE: |
|---|
| 192 | | - seq_puts(seq, "on-line\n"); |
|---|
| 193 | | - break; |
|---|
| 194 | | - default: |
|---|
| 195 | | - seq_puts(seq, "unknown\n"); |
|---|
| 196 | | - break; |
|---|
| 197 | | - } |
|---|
| 198 | | - |
|---|
| 199 | | - return 0; |
|---|
| 200 | | -} |
|---|
| 201 | | - |
|---|
| 202 | | -static int acpi_ac_add_fs(struct acpi_ac *ac) |
|---|
| 203 | | -{ |
|---|
| 204 | | - struct proc_dir_entry *entry = NULL; |
|---|
| 205 | | - |
|---|
| 206 | | - printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded," |
|---|
| 207 | | - " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n"); |
|---|
| 208 | | - if (!acpi_device_dir(ac->device)) { |
|---|
| 209 | | - acpi_device_dir(ac->device) = |
|---|
| 210 | | - proc_mkdir(acpi_device_bid(ac->device), acpi_ac_dir); |
|---|
| 211 | | - if (!acpi_device_dir(ac->device)) |
|---|
| 212 | | - return -ENODEV; |
|---|
| 213 | | - } |
|---|
| 214 | | - |
|---|
| 215 | | - /* 'state' [R] */ |
|---|
| 216 | | - entry = proc_create_single_data(ACPI_AC_FILE_STATE, S_IRUGO, |
|---|
| 217 | | - acpi_device_dir(ac->device), acpi_ac_seq_show, ac); |
|---|
| 218 | | - if (!entry) |
|---|
| 219 | | - return -ENODEV; |
|---|
| 220 | | - return 0; |
|---|
| 221 | | -} |
|---|
| 222 | | - |
|---|
| 223 | | -static int acpi_ac_remove_fs(struct acpi_ac *ac) |
|---|
| 224 | | -{ |
|---|
| 225 | | - |
|---|
| 226 | | - if (acpi_device_dir(ac->device)) { |
|---|
| 227 | | - remove_proc_entry(ACPI_AC_FILE_STATE, |
|---|
| 228 | | - acpi_device_dir(ac->device)); |
|---|
| 229 | | - remove_proc_entry(acpi_device_bid(ac->device), acpi_ac_dir); |
|---|
| 230 | | - acpi_device_dir(ac->device) = NULL; |
|---|
| 231 | | - } |
|---|
| 232 | | - |
|---|
| 233 | | - return 0; |
|---|
| 234 | | -} |
|---|
| 235 | | -#endif |
|---|
| 236 | | - |
|---|
| 237 | 149 | /* -------------------------------------------------------------------------- |
|---|
| 238 | 150 | Driver Model |
|---|
| 239 | 151 | -------------------------------------------------------------------------- */ |
|---|
| .. | .. |
|---|
| 249 | 161 | default: |
|---|
| 250 | 162 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
|---|
| 251 | 163 | "Unsupported event [0x%x]\n", event)); |
|---|
| 252 | | - /* fall through */ |
|---|
| 164 | + fallthrough; |
|---|
| 253 | 165 | case ACPI_AC_NOTIFY_STATUS: |
|---|
| 254 | 166 | case ACPI_NOTIFY_BUS_CHECK: |
|---|
| 255 | 167 | case ACPI_NOTIFY_DEVICE_CHECK: |
|---|
| .. | .. |
|---|
| 306 | 218 | return 0; |
|---|
| 307 | 219 | } |
|---|
| 308 | 220 | |
|---|
| 221 | +static int __init ac_only_quirk(const struct dmi_system_id *d) |
|---|
| 222 | +{ |
|---|
| 223 | + ac_only = 1; |
|---|
| 224 | + return 0; |
|---|
| 225 | +} |
|---|
| 226 | + |
|---|
| 227 | +/* Please keep this list alphabetically sorted */ |
|---|
| 309 | 228 | static const struct dmi_system_id ac_dmi_table[] __initconst = { |
|---|
| 310 | 229 | { |
|---|
| 311 | | - /* Thinkpad e530 */ |
|---|
| 312 | | - .callback = thinkpad_e530_quirk, |
|---|
| 313 | | - .matches = { |
|---|
| 314 | | - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 315 | | - DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), |
|---|
| 316 | | - }, |
|---|
| 317 | | - }, |
|---|
| 318 | | - { |
|---|
| 319 | | - /* ECS EF20EA */ |
|---|
| 230 | + /* ECS EF20EA, AXP288 PMIC but uses separate fuel-gauge */ |
|---|
| 320 | 231 | .callback = ac_do_not_check_pmic_quirk, |
|---|
| 321 | 232 | .matches = { |
|---|
| 322 | 233 | DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"), |
|---|
| 323 | 234 | }, |
|---|
| 324 | 235 | }, |
|---|
| 325 | 236 | { |
|---|
| 326 | | - /* Lenovo Ideapad Miix 320 */ |
|---|
| 237 | + /* Kodlix GK45 returning incorrect state */ |
|---|
| 238 | + .callback = ac_only_quirk, |
|---|
| 239 | + .matches = { |
|---|
| 240 | + DMI_MATCH(DMI_PRODUCT_NAME, "GK45"), |
|---|
| 241 | + }, |
|---|
| 242 | + }, |
|---|
| 243 | + { |
|---|
| 244 | + /* Lenovo Ideapad Miix 320, AXP288 PMIC, separate fuel-gauge */ |
|---|
| 327 | 245 | .callback = ac_do_not_check_pmic_quirk, |
|---|
| 328 | 246 | .matches = { |
|---|
| 329 | | - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 330 | | - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"), |
|---|
| 331 | | - DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), |
|---|
| 247 | + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 248 | + DMI_MATCH(DMI_PRODUCT_NAME, "80XF"), |
|---|
| 249 | + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), |
|---|
| 250 | + }, |
|---|
| 251 | + }, |
|---|
| 252 | + { |
|---|
| 253 | + /* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */ |
|---|
| 254 | + .callback = thinkpad_e530_quirk, |
|---|
| 255 | + .matches = { |
|---|
| 256 | + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 257 | + DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), |
|---|
| 332 | 258 | }, |
|---|
| 333 | 259 | }, |
|---|
| 334 | 260 | {}, |
|---|
| .. | .. |
|---|
| 360 | 286 | psy_cfg.drv_data = ac; |
|---|
| 361 | 287 | |
|---|
| 362 | 288 | ac->charger_desc.name = acpi_device_bid(device); |
|---|
| 363 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 364 | | - result = acpi_ac_add_fs(ac); |
|---|
| 365 | | - if (result) |
|---|
| 366 | | - goto end; |
|---|
| 367 | | -#endif |
|---|
| 368 | 289 | ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS; |
|---|
| 369 | 290 | ac->charger_desc.properties = ac_props; |
|---|
| 370 | 291 | ac->charger_desc.num_properties = ARRAY_SIZE(ac_props); |
|---|
| .. | .. |
|---|
| 384 | 305 | register_acpi_notifier(&ac->battery_nb); |
|---|
| 385 | 306 | end: |
|---|
| 386 | 307 | if (result) { |
|---|
| 387 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 388 | | - acpi_ac_remove_fs(ac); |
|---|
| 389 | | -#endif |
|---|
| 390 | 308 | kfree(ac); |
|---|
| 391 | 309 | } |
|---|
| 392 | 310 | |
|---|
| .. | .. |
|---|
| 430 | 348 | power_supply_unregister(ac->charger); |
|---|
| 431 | 349 | unregister_acpi_notifier(&ac->battery_nb); |
|---|
| 432 | 350 | |
|---|
| 433 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 434 | | - acpi_ac_remove_fs(ac); |
|---|
| 435 | | -#endif |
|---|
| 436 | | - |
|---|
| 437 | 351 | kfree(ac); |
|---|
| 438 | 352 | |
|---|
| 439 | 353 | return 0; |
|---|
| .. | .. |
|---|
| 459 | 373 | } |
|---|
| 460 | 374 | } |
|---|
| 461 | 375 | |
|---|
| 462 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 463 | | - acpi_ac_dir = acpi_lock_ac_dir(); |
|---|
| 464 | | - if (!acpi_ac_dir) |
|---|
| 465 | | - return -ENODEV; |
|---|
| 466 | | -#endif |
|---|
| 467 | | - |
|---|
| 468 | | - |
|---|
| 469 | 376 | result = acpi_bus_register_driver(&acpi_ac_driver); |
|---|
| 470 | 377 | if (result < 0) { |
|---|
| 471 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 472 | | - acpi_unlock_ac_dir(acpi_ac_dir); |
|---|
| 473 | | -#endif |
|---|
| 474 | 378 | return -ENODEV; |
|---|
| 475 | 379 | } |
|---|
| 476 | 380 | |
|---|
| .. | .. |
|---|
| 480 | 384 | static void __exit acpi_ac_exit(void) |
|---|
| 481 | 385 | { |
|---|
| 482 | 386 | acpi_bus_unregister_driver(&acpi_ac_driver); |
|---|
| 483 | | -#ifdef CONFIG_ACPI_PROCFS_POWER |
|---|
| 484 | | - acpi_unlock_ac_dir(acpi_ac_dir); |
|---|
| 485 | | -#endif |
|---|
| 486 | 387 | } |
|---|
| 487 | 388 | module_init(acpi_ac_init); |
|---|
| 488 | 389 | module_exit(acpi_ac_exit); |
|---|