GNU Linux-libre 4.14.265-gnu1
[releases.git] / drivers / mfd / cros_ec_acpi_gpe.c
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
23 static 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  */
29 static 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  */
38 static 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
64 int 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
84 void 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
97 void 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 }