linux/drivers/mfd/cros_ec_acpi_gpe.c
<<
>>
Prefs
   1/*
   2 * ChromeOS EC multi-function device
   3 *
   4 * Copyright (C) 2017 Google, Inc
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * The ChromeOS EC multi function device is used to mux all the requests
  16 * to the EC device for its multiple features: keyboard controller,
  17 * battery charging and regulator control, firmware update.
  18 */
  19#include <linux/acpi.h>
  20
  21#define ACPI_LID_DEVICE      "LID0"
  22
  23static int ec_wake_gpe = -EINVAL;
  24
  25/*
  26 * This handler indicates to ACPI core that this GPE should stay enabled for
  27 * lid to work in suspend to idle path.
  28 */
  29static u32 cros_ec_gpe_handler(acpi_handle gpe_device, u32 gpe_number,
  30                               void *data)
  31{
  32        return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
  33}
  34
  35/*
  36 * Get ACPI GPE for LID0 device.
  37 */
  38static int cros_ec_get_ec_wake_gpe(struct device *dev)
  39{
  40        struct acpi_device *cros_acpi_dev;
  41        struct acpi_device *adev;
  42        acpi_handle handle;
  43        acpi_status status;
  44        int ret;
  45
  46        cros_acpi_dev = ACPI_COMPANION(dev);
  47
  48        if (!cros_acpi_dev || !cros_acpi_dev->parent ||
  49           !cros_acpi_dev->parent->handle)
  50                return -EINVAL;
  51
  52        status = acpi_get_handle(cros_acpi_dev->parent->handle, ACPI_LID_DEVICE,
  53                                 &handle);
  54        if (ACPI_FAILURE(status))
  55                return -EINVAL;
  56
  57        ret = acpi_bus_get_device(handle, &adev);
  58        if (ret)
  59                return ret;
  60
  61        return adev->wakeup.gpe_number;
  62}
  63
  64int cros_ec_acpi_install_gpe_handler(struct device *dev)
  65{
  66        acpi_status status;
  67
  68        ec_wake_gpe = cros_ec_get_ec_wake_gpe(dev);
  69
  70        if (ec_wake_gpe < 0)
  71                return ec_wake_gpe;
  72
  73        status = acpi_install_gpe_handler(NULL, ec_wake_gpe,
  74                                          ACPI_GPE_EDGE_TRIGGERED,
  75                                          &cros_ec_gpe_handler, NULL);
  76        if (ACPI_FAILURE(status))
  77                return -ENODEV;
  78
  79        dev_info(dev, "Initialized, GPE = 0x%x\n", ec_wake_gpe);
  80
  81        return 0;
  82}
  83
  84void cros_ec_acpi_remove_gpe_handler(void)
  85{
  86        acpi_status status;
  87
  88        if (ec_wake_gpe < 0)
  89                return;
  90
  91        status = acpi_remove_gpe_handler(NULL, ec_wake_gpe,
  92                                                 &cros_ec_gpe_handler);
  93        if (ACPI_FAILURE(status))
  94                pr_err("failed to remove gpe handler\n");
  95}
  96
  97void cros_ec_acpi_clear_gpe(void)
  98{
  99        if (ec_wake_gpe < 0)
 100                return;
 101
 102        acpi_clear_gpe(NULL, ec_wake_gpe);
 103}
 104