blob: 56d305dab2d487f7b07b8c9f5424320823aea03a [file] [log] [blame]
/*
* 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);
}