/* * ChromeOS EC multi-function device * * Copyright (C) 2017 Google, Inc * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * The ChromeOS EC multi function device is used to mux all the requests * to the EC device for its multiple features: keyboard controller, * battery charging and regulator control, firmware update. */ #include #define ACPI_LID_DEVICE "LID0" static int ec_wake_gpe = -EINVAL; /* * This handler indicates to ACPI core that this GPE should stay enabled for * lid to work in suspend to idle path. */ static u32 cros_ec_gpe_handler(acpi_handle gpe_device, u32 gpe_number, void *data) { return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; } /* * Get ACPI GPE for LID0 device. */ static int cros_ec_get_ec_wake_gpe(struct device *dev) { struct acpi_device *cros_acpi_dev; struct acpi_device *adev; acpi_handle handle; acpi_status status; int ret; cros_acpi_dev = ACPI_COMPANION(dev); if (!cros_acpi_dev || !cros_acpi_dev->parent || !cros_acpi_dev->parent->handle) return -EINVAL; status = acpi_get_handle(cros_acpi_dev->parent->handle, ACPI_LID_DEVICE, &handle); if (ACPI_FAILURE(status)) return -EINVAL; ret = acpi_bus_get_device(handle, &adev); if (ret) return ret; return adev->wakeup.gpe_number; } int cros_ec_acpi_install_gpe_handler(struct device *dev) { acpi_status status; ec_wake_gpe = cros_ec_get_ec_wake_gpe(dev); if (ec_wake_gpe < 0) return ec_wake_gpe; status = acpi_install_gpe_handler(NULL, ec_wake_gpe, ACPI_GPE_EDGE_TRIGGERED, &cros_ec_gpe_handler, NULL); if (ACPI_FAILURE(status)) return -ENODEV; dev_info(dev, "Initialized, GPE = 0x%x\n", ec_wake_gpe); return 0; } void cros_ec_acpi_remove_gpe_handler(void) { acpi_status status; if (ec_wake_gpe < 0) return; status = acpi_remove_gpe_handler(NULL, ec_wake_gpe, &cros_ec_gpe_handler); if (ACPI_FAILURE(status)) pr_err("failed to remove gpe handler\n"); } void cros_ec_acpi_clear_gpe(void) { if (ec_wake_gpe < 0) return; acpi_clear_gpe(NULL, ec_wake_gpe); }