104 lines
2.4 KiB
C
104 lines
2.4 KiB
C
/*
|
|
* 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 <linux/acpi.h>
|
|
|
|
#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);
|
|
}
|