This repository has been archived by the owner on Apr 23, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
0003-acpi-button-Power-button-release-implementation-via-.patch
250 lines (231 loc) · 7.96 KB
/
0003-acpi-button-Power-button-release-implementation-via-.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
From 221c1c78f5bfa1f94a35a3e35038d96f426fe9a2 Mon Sep 17 00:00:00 2001
From: Andy Ross <andrew.j.ross@intel.com>
Date: Thu, 12 Dec 2013 10:42:00 -0800
Subject: [PATCH 3/3] acpi button: Power button release implementation via _DSM
Newer firmware on Intel boards exposes an interface for getting
"release" events from power buttons. When possible this can be done
via an ACPI notification on the same PWRB object, and when not the
firmware can expose a polling method which can be integrated with the
existing polling logic for legacy PCH hardware.
Expose runtime enablers for these features via "dsm_poll" and
"dsm_notify" parameters.
Issue: APDEV-575
Orig-Change-Id: I517fdb3a02c6c2ce3bc6ca96eb3d31f04889af3d
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
Conflicts:
drivers/acpi/button.c
Change-Id: Id6e97dee68351e31baad558d337b708e640fdc9e
Signed-off-by: Hakan Englund <hakan.englund@intel.com>
---
drivers/acpi/button.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 122 insertions(+), 10 deletions(-)
--- linux.orig/drivers/acpi/button.c
+++ linux/drivers/acpi/button.c
@@ -57,6 +57,22 @@
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
#define ACPI_BUTTON_TYPE_LID 0x05
+/* Intel-defined power button interface implemented via the _DSM
+ * method on the PWRB object. It has three functions as of version 0:
+ * the first returns a bitmask of supported functions, the second
+ * registers to receive an ACPI notification on power button release
+ * events, and the third is a synchronous poll of power button state.
+ * Not all systems will support notification. */
+#define ACPI_PWRB_DSM_UUID "9c355bcb-35fa-44f7-8a67-447359c36a03"
+#define ACPI_PWRB_DSM_VERSION 0
+#define ACPI_PWRB_DSM_RELEASE 0xc0
+
+enum {
+ ACPI_PWRB_DSM_PROBE = 0,
+ ACPI_PWRB_DSM_REGISTER = 1,
+ ACPI_PWRB_DSM_LEVEL = 2,
+};
+
#define _COMPONENT ACPI_BUTTON_COMPONENT
ACPI_MODULE_NAME("button");
@@ -64,6 +80,14 @@ MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI Button Driver");
MODULE_LICENSE("GPL");
+static bool dsm_notify = true;
+module_param(dsm_notify, bool, 0644);
+MODULE_PARM_DESC(dsm_notify, "Enable _DSM notification of power button release");
+
+static bool dsm_poll = true;
+module_param(dsm_poll, bool, 0644);
+MODULE_PARM_DESC(dsm_poll, "Enable _DSM polling for power button release");
+
static const struct acpi_device_id button_device_ids[] = {
{ACPI_BUTTON_HID_LID, 0},
{ACPI_BUTTON_HID_SLEEP, 0},
@@ -99,9 +123,12 @@ static struct acpi_driver acpi_button_dr
struct acpi_button {
unsigned int type;
+ struct acpi_device *acpi_dev;
struct input_dev *input;
char phys[32]; /* for input device */
unsigned long pushed;
+ bool dsm_notify;
+ bool dsm_poll;
};
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
@@ -230,6 +257,80 @@ static int acpi_button_remove_fs(struct
/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
+
+static int pwrb_dsm_call(acpi_handle *handle, int func)
+{
+ u8 uuid[16];
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list olist;
+ union acpi_object args[4], *out;
+ acpi_status err;
+ int i, ret = -1;
+
+ acpi_str_to_uuid(ACPI_PWRB_DSM_UUID, uuid);
+ args[0].type = ACPI_TYPE_BUFFER;
+ args[0].buffer.length = sizeof(uuid);
+ args[0].buffer.pointer = uuid;
+
+ args[1].type = ACPI_TYPE_INTEGER;
+ args[1].integer.value = ACPI_PWRB_DSM_VERSION;
+
+ args[2].type = ACPI_TYPE_INTEGER;
+ args[2].integer.value = func;
+
+ args[3].type = ACPI_TYPE_PACKAGE;
+ args[3].package.count = 0;
+ args[3].package.elements = NULL;
+
+ olist.count = ARRAY_SIZE(args);
+ olist.pointer = args;
+
+ if (func == ACPI_PWRB_DSM_PROBE) {
+ /* PROBE returns a bitmask of supported functions as a
+ * buffer. Return a failure here as 0: method not
+ * present or not functioning correctly means "no
+ * functions supported" */
+ ret = 0;
+ err = acpi_evaluate_object(handle, "_DSM", &olist, &buf);
+ out = buf.pointer;
+ if (!ACPI_FAILURE(err) && out->type == ACPI_TYPE_BUFFER) {
+ for (i = 0; i < out->buffer.length; i++)
+ ret |= (out->buffer.pointer[i] << (i*8));
+ kfree(buf.pointer);
+ }
+ } else {
+ unsigned long long acpiret;
+ err = acpi_evaluate_integer(handle, "_DSM", &olist, &acpiret);
+ if (ACPI_FAILURE(err))
+ ret = -1;
+ ret = (int)acpiret;
+ }
+
+ return ret;
+}
+
+static void pwrb_dsm_init(struct acpi_button *btn)
+{
+ struct acpi_device *dev = btn->acpi_dev;
+ int funcs = pwrb_dsm_call(dev->handle, ACPI_PWRB_DSM_PROBE);
+
+ if (dsm_notify && (funcs & ACPI_PWRB_DSM_REGISTER)) {
+ if (pwrb_dsm_call(dev->handle, ACPI_PWRB_DSM_REGISTER))
+ dev_err(&dev->dev, "PWRB release notification registration failed");
+ else
+ btn->dsm_notify = true;
+ }
+
+ if (dsm_poll && !btn->dsm_notify && (funcs & ACPI_PWRB_DSM_LEVEL))
+ btn->dsm_poll = true;
+}
+
+static int pwrb_dsm_poll(struct acpi_button *button)
+{
+ struct acpi_device *dev = button->acpi_dev;
+ return pwrb_dsm_call(dev->handle, ACPI_PWRB_DSM_LEVEL);
+}
+
int acpi_lid_notifier_register(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
@@ -294,18 +395,20 @@ static int acpi_lid_send_state(struct ac
static void pwrbtn_timer(unsigned long arg)
{
struct acpi_button *button = (struct acpi_button *)arg;
- int toolong, pressed;
+ int pressed;
+ int toolong = (jiffies - pwrbtn_poll->started) > PWRBTN_POLL_MAX;
unsigned long flags;
- spin_lock_irqsave(&pwrbtn_lock, flags);
- pressed = pwrbtn_poll->poll(pwrbtn_poll);
- spin_unlock_irqrestore(&pwrbtn_lock, flags);
-
- toolong = (jiffies - pwrbtn_poll->started) > PWRBTN_POLL_MAX;
-
if (toolong)
dev_err(&button->input->dev, "Power button poll failed to detect release");
+ spin_lock_irqsave(&pwrbtn_lock, flags);
+ if (button->dsm_poll)
+ pressed = pwrb_dsm_poll(button);
+ else
+ pressed = pwrbtn_poll->poll(pwrbtn_poll);
+ spin_unlock_irqrestore(&pwrbtn_lock, flags);
+
if (!pressed || toolong) {
input_report_key(button->input, KEY_POWER, 0);
input_sync(button->input);
@@ -346,6 +449,7 @@ int acpi_pwrbtn_poll_unregister(struct a
static int start_pwrbtn_poll(struct acpi_button *button)
{
+ int ret;
pwrbtn_poll->timer.data = (unsigned long)button;
pwrbtn_poll->started = jiffies;
return mod_timer(&pwrbtn_poll->timer, jiffies + PWRBTN_POLL_JIFFIES);
@@ -367,19 +471,20 @@ static void acpi_button_notify(struct ac
} else {
int keycode = test_bit(KEY_SLEEP, input->keybit) ?
KEY_SLEEP : KEY_POWER;
+ bool pwr = button->type == ACPI_BUTTON_TYPE_POWER;
input_report_key(input, keycode, 1);
input_sync(input);
- if (button->type == ACPI_BUTTON_TYPE_POWER
- && pwrbtn_poll) {
+ if (pwr && !button->dsm_notify
+ && (pwrbtn_poll || button->dsm_poll)) {
if (start_pwrbtn_poll(button)) {
/* Error, fall back to
* synchronous event */
input_report_key(input, keycode, 0);
input_sync(input);
}
- } else {
+ } else if (!pwr || !button->dsm_notify) {
/* No release detection; emit the UP
* synchronously */
input_report_key(input, keycode, 0);
@@ -393,6 +498,10 @@ static void acpi_button_notify(struct ac
event, ++button->pushed);
}
break;
+ case ACPI_PWRB_DSM_RELEASE:
+ input_report_key(button->input, KEY_POWER, 0);
+ input_sync(button->input);
+ break;
default:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Unsupported event [0x%x]\n", event));
@@ -424,6 +533,7 @@ static int acpi_button_add(struct acpi_d
if (!button)
return -ENOMEM;
+ button->acpi_dev = device;
device->driver_data = button;
button->input = input = input_allocate_device();
@@ -441,6 +551,8 @@ static int acpi_button_add(struct acpi_d
strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
+ pwrb_dsm_init(button);
+
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
button->type = ACPI_BUTTON_TYPE_SLEEP;