cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ac.c (7739B)


      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
     35static int acpi_ac_add(struct acpi_device *device);
     36static int acpi_ac_remove(struct acpi_device *device);
     37static void acpi_ac_notify(struct acpi_device *device, u32 event);
     38
     39struct acpi_ac_bl {
     40	const char *hid;
     41	int hrv;
     42};
     43
     44static const struct acpi_device_id ac_device_ids[] = {
     45	{"ACPI0003", 0},
     46	{"", 0},
     47};
     48MODULE_DEVICE_TABLE(acpi, ac_device_ids);
     49
     50#ifdef CONFIG_PM_SLEEP
     51static int acpi_ac_resume(struct device *dev);
     52#endif
     53static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
     54
     55static int ac_sleep_before_get_state_ms;
     56static int ac_only;
     57
     58static struct acpi_driver acpi_ac_driver = {
     59	.name = "ac",
     60	.class = ACPI_AC_CLASS,
     61	.ids = ac_device_ids,
     62	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
     63	.ops = {
     64		.add = acpi_ac_add,
     65		.remove = acpi_ac_remove,
     66		.notify = acpi_ac_notify,
     67		},
     68	.drv.pm = &acpi_ac_pm,
     69};
     70
     71struct acpi_ac {
     72	struct power_supply *charger;
     73	struct power_supply_desc charger_desc;
     74	struct acpi_device *device;
     75	unsigned long long state;
     76	struct notifier_block battery_nb;
     77};
     78
     79#define to_acpi_ac(x) power_supply_get_drvdata(x)
     80
     81/* AC Adapter Management */
     82static int acpi_ac_get_state(struct acpi_ac *ac)
     83{
     84	acpi_status status = AE_OK;
     85
     86	if (!ac)
     87		return -EINVAL;
     88
     89	if (ac_only) {
     90		ac->state = 1;
     91		return 0;
     92	}
     93
     94	status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
     95				       &ac->state);
     96	if (ACPI_FAILURE(status)) {
     97		acpi_handle_info(ac->device->handle,
     98				"Error reading AC Adapter state: %s\n",
     99				acpi_format_exception(status));
    100		ac->state = ACPI_AC_STATUS_UNKNOWN;
    101		return -ENODEV;
    102	}
    103
    104	return 0;
    105}
    106
    107/* sysfs I/F */
    108static int get_ac_property(struct power_supply *psy,
    109			   enum power_supply_property psp,
    110			   union power_supply_propval *val)
    111{
    112	struct acpi_ac *ac = to_acpi_ac(psy);
    113
    114	if (!ac)
    115		return -ENODEV;
    116
    117	if (acpi_ac_get_state(ac))
    118		return -ENODEV;
    119
    120	switch (psp) {
    121	case POWER_SUPPLY_PROP_ONLINE:
    122		val->intval = ac->state;
    123		break;
    124	default:
    125		return -EINVAL;
    126	}
    127
    128	return 0;
    129}
    130
    131static enum power_supply_property ac_props[] = {
    132	POWER_SUPPLY_PROP_ONLINE,
    133};
    134
    135/* Driver Model */
    136static void acpi_ac_notify(struct acpi_device *device, u32 event)
    137{
    138	struct acpi_ac *ac = acpi_driver_data(device);
    139
    140	if (!ac)
    141		return;
    142
    143	switch (event) {
    144	default:
    145		acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
    146				  event);
    147		fallthrough;
    148	case ACPI_AC_NOTIFY_STATUS:
    149	case ACPI_NOTIFY_BUS_CHECK:
    150	case ACPI_NOTIFY_DEVICE_CHECK:
    151		/*
    152		 * A buggy BIOS may notify AC first and then sleep for
    153		 * a specific time before doing actual operations in the
    154		 * EC event handler (_Qxx). This will cause the AC state
    155		 * reported by the ACPI event to be incorrect, so wait for a
    156		 * specific time for the EC event handler to make progress.
    157		 */
    158		if (ac_sleep_before_get_state_ms > 0)
    159			msleep(ac_sleep_before_get_state_ms);
    160
    161		acpi_ac_get_state(ac);
    162		acpi_bus_generate_netlink_event(device->pnp.device_class,
    163						  dev_name(&device->dev), event,
    164						  (u32) ac->state);
    165		acpi_notifier_call_chain(device, event, (u32) ac->state);
    166		kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
    167	}
    168}
    169
    170static int acpi_ac_battery_notify(struct notifier_block *nb,
    171				  unsigned long action, void *data)
    172{
    173	struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb);
    174	struct acpi_bus_event *event = (struct acpi_bus_event *)data;
    175
    176	/*
    177	 * On HP Pavilion dv6-6179er AC status notifications aren't triggered
    178	 * when adapter is plugged/unplugged. However, battery status
    179	 * notifications are triggered when battery starts charging or
    180	 * discharging. Re-reading AC status triggers lost AC notifications,
    181	 * if AC status has changed.
    182	 */
    183	if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 &&
    184	    event->type == ACPI_BATTERY_NOTIFY_STATUS)
    185		acpi_ac_get_state(ac);
    186
    187	return NOTIFY_OK;
    188}
    189
    190static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
    191{
    192	ac_sleep_before_get_state_ms = 1000;
    193	return 0;
    194}
    195
    196static int __init ac_only_quirk(const struct dmi_system_id *d)
    197{
    198	ac_only = 1;
    199	return 0;
    200}
    201
    202/* Please keep this list alphabetically sorted */
    203static const struct dmi_system_id ac_dmi_table[]  __initconst = {
    204	{
    205		/* Kodlix GK45 returning incorrect state */
    206		.callback = ac_only_quirk,
    207		.matches = {
    208			DMI_MATCH(DMI_PRODUCT_NAME, "GK45"),
    209		},
    210	},
    211	{
    212		/* Lenovo Thinkpad e530, see comment in acpi_ac_notify() */
    213		.callback = thinkpad_e530_quirk,
    214		.matches = {
    215			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
    216			DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
    217		},
    218	},
    219	{},
    220};
    221
    222static int acpi_ac_add(struct acpi_device *device)
    223{
    224	struct power_supply_config psy_cfg = {};
    225	int result = 0;
    226	struct acpi_ac *ac = NULL;
    227
    228
    229	if (!device)
    230		return -EINVAL;
    231
    232	ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
    233	if (!ac)
    234		return -ENOMEM;
    235
    236	ac->device = device;
    237	strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
    238	strcpy(acpi_device_class(device), ACPI_AC_CLASS);
    239	device->driver_data = ac;
    240
    241	result = acpi_ac_get_state(ac);
    242	if (result)
    243		goto end;
    244
    245	psy_cfg.drv_data = ac;
    246
    247	ac->charger_desc.name = acpi_device_bid(device);
    248	ac->charger_desc.type = POWER_SUPPLY_TYPE_MAINS;
    249	ac->charger_desc.properties = ac_props;
    250	ac->charger_desc.num_properties = ARRAY_SIZE(ac_props);
    251	ac->charger_desc.get_property = get_ac_property;
    252	ac->charger = power_supply_register(&ac->device->dev,
    253					    &ac->charger_desc, &psy_cfg);
    254	if (IS_ERR(ac->charger)) {
    255		result = PTR_ERR(ac->charger);
    256		goto end;
    257	}
    258
    259	pr_info("%s [%s] (%s)\n", acpi_device_name(device),
    260		acpi_device_bid(device), ac->state ? "on-line" : "off-line");
    261
    262	ac->battery_nb.notifier_call = acpi_ac_battery_notify;
    263	register_acpi_notifier(&ac->battery_nb);
    264end:
    265	if (result)
    266		kfree(ac);
    267
    268	return result;
    269}
    270
    271#ifdef CONFIG_PM_SLEEP
    272static int acpi_ac_resume(struct device *dev)
    273{
    274	struct acpi_ac *ac;
    275	unsigned int old_state;
    276
    277	if (!dev)
    278		return -EINVAL;
    279
    280	ac = acpi_driver_data(to_acpi_device(dev));
    281	if (!ac)
    282		return -EINVAL;
    283
    284	old_state = ac->state;
    285	if (acpi_ac_get_state(ac))
    286		return 0;
    287	if (old_state != ac->state)
    288		kobject_uevent(&ac->charger->dev.kobj, KOBJ_CHANGE);
    289
    290	return 0;
    291}
    292#else
    293#define acpi_ac_resume NULL
    294#endif
    295
    296static int acpi_ac_remove(struct acpi_device *device)
    297{
    298	struct acpi_ac *ac = NULL;
    299
    300	if (!device || !acpi_driver_data(device))
    301		return -EINVAL;
    302
    303	ac = acpi_driver_data(device);
    304
    305	power_supply_unregister(ac->charger);
    306	unregister_acpi_notifier(&ac->battery_nb);
    307
    308	kfree(ac);
    309
    310	return 0;
    311}
    312
    313static int __init acpi_ac_init(void)
    314{
    315	int result;
    316
    317	if (acpi_disabled)
    318		return -ENODEV;
    319
    320	if (acpi_quirk_skip_acpi_ac_and_battery())
    321		return -ENODEV;
    322
    323	dmi_check_system(ac_dmi_table);
    324
    325	result = acpi_bus_register_driver(&acpi_ac_driver);
    326	if (result < 0)
    327		return -ENODEV;
    328
    329	return 0;
    330}
    331
    332static void __exit acpi_ac_exit(void)
    333{
    334	acpi_bus_unregister_driver(&acpi_ac_driver);
    335}
    336module_init(acpi_ac_init);
    337module_exit(acpi_ac_exit);