Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * 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);
}