blob: 7f69d8d1905b8af1956a064f4d955b7bfad31d2e [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Bjorn Helgaas50a4da82009-04-08 15:39:38 +00003 * button.c - ACPI Button Driver
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
6 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 */
8
Vincent Legollb6aeab42017-05-20 20:38:13 +02009#define pr_fmt(fmt) "ACPI: button: " fmt
Lv Zhengdfa46c52016-08-17 16:22:58 +080010
Randy Dunlap2c4c2a72018-07-07 08:25:01 -070011#include <linux/compiler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -040015#include <linux/types.h>
16#include <linux/proc_fs.h>
17#include <linux/seq_file.h>
Dmitry Torokhovc0968f02006-11-09 00:40:13 -050018#include <linux/input.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090019#include <linux/slab.h>
Lv Zheng8b484632013-12-03 08:49:16 +080020#include <linux/acpi.h>
Hans de Goede9e811e12017-11-22 16:06:12 +010021#include <linux/dmi.h>
Andy Shevchenko6270da62013-03-11 09:17:04 +000022#include <acpi/button.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Len Browna192a952009-07-28 16:45:54 -040024#define PREFIX "ACPI: "
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#define ACPI_BUTTON_CLASS "button"
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -040027#define ACPI_BUTTON_FILE_INFO "info"
28#define ACPI_BUTTON_FILE_STATE "state"
29#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#define ACPI_BUTTON_NOTIFY_STATUS 0x80
31
32#define ACPI_BUTTON_SUBCLASS_POWER "power"
Len Brown4be44fc2005-08-05 00:44:28 -040033#define ACPI_BUTTON_HID_POWER "PNP0C0C"
Bjorn Helgaasd68b5972009-04-08 15:40:04 +000034#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#define ACPI_BUTTON_TYPE_POWER 0x01
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
37#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
38#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
Bjorn Helgaasd68b5972009-04-08 15:40:04 +000039#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#define ACPI_BUTTON_TYPE_SLEEP 0x03
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#define ACPI_BUTTON_SUBCLASS_LID "lid"
43#define ACPI_BUTTON_HID_LID "PNP0C0D"
44#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
45#define ACPI_BUTTON_TYPE_LID 0x05
46
Hans de Goede065bd4d2019-10-26 22:24:31 +020047enum {
48 ACPI_BUTTON_LID_INIT_IGNORE,
49 ACPI_BUTTON_LID_INIT_OPEN,
50 ACPI_BUTTON_LID_INIT_METHOD,
Hans de Goede593681e2019-10-26 22:24:32 +020051 ACPI_BUTTON_LID_INIT_DISABLED,
Hans de Goede065bd4d2019-10-26 22:24:31 +020052};
53
54static const char * const lid_init_state_str[] = {
55 [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore",
56 [ACPI_BUTTON_LID_INIT_OPEN] = "open",
57 [ACPI_BUTTON_LID_INIT_METHOD] = "method",
Hans de Goede593681e2019-10-26 22:24:32 +020058 [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled",
Hans de Goede065bd4d2019-10-26 22:24:31 +020059};
Lv Zheng3540c322016-06-01 18:10:48 +080060
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#define _COMPONENT ACPI_BUTTON_COMPONENT
Len Brownf52fd662007-02-12 22:42:12 -050062ACPI_MODULE_NAME("button");
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Dmitry Torokhovc0968f02006-11-09 00:40:13 -050064MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050065MODULE_DESCRIPTION("ACPI Button Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070066MODULE_LICENSE("GPL");
67
Thomas Renninger1ba90e32007-07-23 14:44:41 +020068static const struct acpi_device_id button_device_ids[] = {
69 {ACPI_BUTTON_HID_LID, 0},
70 {ACPI_BUTTON_HID_SLEEP, 0},
71 {ACPI_BUTTON_HID_SLEEPF, 0},
72 {ACPI_BUTTON_HID_POWER, 0},
73 {ACPI_BUTTON_HID_POWERF, 0},
74 {"", 0},
75};
76MODULE_DEVICE_TABLE(acpi, button_device_ids);
77
Hans de Goede9e811e12017-11-22 16:06:12 +010078/*
79 * Some devices which don't even have a lid in anyway have a broken _LID
80 * method (e.g. pointing to a floating gpio pin) causing spurious LID events.
81 */
82static const struct dmi_system_id lid_blacklst[] = {
83 {
84 /* GP-electronic T701 */
85 .matches = {
86 DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
87 DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
88 DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
89 },
90 },
91 {}
92};
93
Len Brown4be44fc2005-08-05 00:44:28 -040094static int acpi_button_add(struct acpi_device *device);
Rafael J. Wysocki51fac832013-01-24 00:24:48 +010095static int acpi_button_remove(struct acpi_device *device);
Bjorn Helgaas373cfc32009-03-30 17:48:18 +000096static void acpi_button_notify(struct acpi_device *device, u32 event);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
Rafael J. Wysocki90692402012-08-09 23:00:02 +020098#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +020099static int acpi_button_suspend(struct device *dev);
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200100static int acpi_button_resume(struct device *dev);
Shuah Khan2de9fd12014-02-12 20:19:07 -0700101#else
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200102#define acpi_button_suspend NULL
Shuah Khan2de9fd12014-02-12 20:19:07 -0700103#define acpi_button_resume NULL
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200104#endif
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200105static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107static struct acpi_driver acpi_button_driver = {
Len Brownc2b67052007-02-12 23:33:40 -0500108 .name = "button",
Len Brown4be44fc2005-08-05 00:44:28 -0400109 .class = ACPI_BUTTON_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200110 .ids = button_device_ids,
Len Brown4be44fc2005-08-05 00:44:28 -0400111 .ops = {
112 .add = acpi_button_add,
113 .remove = acpi_button_remove,
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000114 .notify = acpi_button_notify,
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500115 },
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200116 .drv.pm = &acpi_button_pm,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117};
118
119struct acpi_button {
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500120 unsigned int type;
121 struct input_dev *input;
122 char phys[32]; /* for input device */
Len Brown4be44fc2005-08-05 00:44:28 -0400123 unsigned long pushed;
Lv Zhengdfa46c52016-08-17 16:22:58 +0800124 int last_state;
125 ktime_t last_time;
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200126 bool suspended;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127};
128
Jesse Barnes7e127152009-09-10 15:28:02 -0700129static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
130static struct acpi_device *lid_device;
Benjamin Tissoires878d8db02017-05-10 18:12:40 +0200131static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
Jesse Barnes7e127152009-09-10 15:28:02 -0700132
Lv Zhengdfa46c52016-08-17 16:22:58 +0800133static unsigned long lid_report_interval __read_mostly = 500;
134module_param(lid_report_interval, ulong, 0644);
135MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
136
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400137/* --------------------------------------------------------------------------
138 FS Interface (/proc)
139 -------------------------------------------------------------------------- */
140
Len Brown4be44fc2005-08-05 00:44:28 -0400141static struct proc_dir_entry *acpi_button_dir;
Zhang Rui912b7422011-03-23 10:21:40 +0800142static struct proc_dir_entry *acpi_lid_dir;
Len Brown4be44fc2005-08-05 00:44:28 -0400143
Lv Zhengee7e2262016-06-01 18:10:42 +0800144static int acpi_lid_evaluate_state(struct acpi_device *device)
145{
146 unsigned long long lid_state;
147 acpi_status status;
148
149 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
150 if (ACPI_FAILURE(status))
151 return -ENODEV;
152
153 return lid_state ? 1 : 0;
154}
155
156static int acpi_lid_notify_state(struct acpi_device *device, int state)
157{
158 struct acpi_button *button = acpi_driver_data(device);
159 int ret;
Lv Zhengdfa46c52016-08-17 16:22:58 +0800160 ktime_t next_report;
161 bool do_update;
Lv Zhengee7e2262016-06-01 18:10:42 +0800162
Lv Zhengdfa46c52016-08-17 16:22:58 +0800163 /*
164 * In lid_init_state=ignore mode, if user opens/closes lid
165 * frequently with "open" missing, and "last_time" is also updated
166 * frequently, "close" cannot be delivered to the userspace.
167 * So "last_time" is only updated after a timeout or an actual
168 * switch.
169 */
170 if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
171 button->last_state != !!state)
172 do_update = true;
173 else
174 do_update = false;
175
176 next_report = ktime_add(button->last_time,
177 ms_to_ktime(lid_report_interval));
178 if (button->last_state == !!state &&
179 ktime_after(ktime_get(), next_report)) {
180 /* Complain the buggy firmware */
181 pr_warn_once("The lid device is not compliant to SW_LID.\n");
182
183 /*
184 * Send the unreliable complement switch event:
185 *
186 * On most platforms, the lid device is reliable. However
187 * there are exceptions:
188 * 1. Platforms returning initial lid state as "close" by
189 * default after booting/resuming:
190 * https://bugzilla.kernel.org/show_bug.cgi?id=89211
191 * https://bugzilla.kernel.org/show_bug.cgi?id=106151
192 * 2. Platforms never reporting "open" events:
193 * https://bugzilla.kernel.org/show_bug.cgi?id=106941
194 * On these buggy platforms, the usage model of the ACPI
195 * lid device actually is:
196 * 1. The initial returning value of _LID may not be
197 * reliable.
198 * 2. The open event may not be reliable.
199 * 3. The close event is reliable.
200 *
201 * But SW_LID is typed as input switch event, the input
202 * layer checks if the event is redundant. Hence if the
203 * state is not switched, the userspace cannot see this
204 * platform triggered reliable event. By inserting a
205 * complement switch event, it then is guaranteed that the
206 * platform triggered reliable one can always be seen by
207 * the userspace.
208 */
209 if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
210 do_update = true;
211 /*
212 * Do generate complement switch event for "close"
213 * as "close" is reliable and wrong "open" won't
214 * trigger unexpected behaviors.
215 * Do not generate complement switch event for
216 * "open" as "open" is not reliable and wrong
217 * "close" will trigger unexpected behaviors.
218 */
219 if (!state) {
220 input_report_switch(button->input,
221 SW_LID, state);
222 input_sync(button->input);
223 }
224 }
225 }
226 /* Send the platform triggered reliable event */
227 if (do_update) {
Hans de Goedeae35d652017-11-22 16:06:11 +0100228 acpi_handle_debug(device->handle, "ACPI LID %s\n",
229 state ? "open" : "closed");
Lv Zhengdfa46c52016-08-17 16:22:58 +0800230 input_report_switch(button->input, SW_LID, !state);
231 input_sync(button->input);
232 button->last_state = !!state;
233 button->last_time = ktime_get();
234 }
Lv Zhengee7e2262016-06-01 18:10:42 +0800235
Lv Zhengee7e2262016-06-01 18:10:42 +0800236 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
237 if (ret == NOTIFY_DONE)
238 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
239 device);
240 if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
241 /*
242 * It is also regarded as success if the notifier_chain
243 * returns NOTIFY_OK or NOTIFY_DONE.
244 */
245 ret = 0;
246 }
247 return ret;
248}
249
Randy Dunlap2c4c2a72018-07-07 08:25:01 -0700250static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
251 void *offset)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400252{
Bjorn Helgaas106c19e2009-04-08 15:39:59 +0000253 struct acpi_device *device = seq->private;
Lv Zhengee7e2262016-06-01 18:10:42 +0800254 int state;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400255
Lv Zhengee7e2262016-06-01 18:10:42 +0800256 state = acpi_lid_evaluate_state(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500257 seq_printf(seq, "state: %s\n",
Lv Zhengee7e2262016-06-01 18:10:42 +0800258 state < 0 ? "unsupported" : (state ? "open" : "closed"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400259 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400260}
261
Len Brown4be44fc2005-08-05 00:44:28 -0400262static int acpi_button_add_fs(struct acpi_device *device)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400263{
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000264 struct acpi_button *button = acpi_driver_data(device);
Len Brown4be44fc2005-08-05 00:44:28 -0400265 struct proc_dir_entry *entry = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800266 int ret = 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400267
Zhang Rui912b7422011-03-23 10:21:40 +0800268 /* procfs I/F for ACPI lid device only */
269 if (button->type != ACPI_BUTTON_TYPE_LID)
270 return 0;
271
272 if (acpi_button_dir || acpi_lid_dir) {
273 printk(KERN_ERR PREFIX "More than one Lid device found!\n");
274 return -EEXIST;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400275 }
276
Zhang Rui912b7422011-03-23 10:21:40 +0800277 /* create /proc/acpi/button */
278 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
279 if (!acpi_button_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -0400280 return -ENODEV;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400281
Zhang Rui912b7422011-03-23 10:21:40 +0800282 /* create /proc/acpi/button/lid */
283 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
284 if (!acpi_lid_dir) {
285 ret = -ENODEV;
286 goto remove_button_dir;
287 }
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400288
Zhang Rui912b7422011-03-23 10:21:40 +0800289 /* create /proc/acpi/button/lid/LID/ */
290 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
291 if (!acpi_device_dir(device)) {
292 ret = -ENODEV;
293 goto remove_lid_dir;
294 }
295
296 /* create /proc/acpi/button/lid/LID/state */
Christoph Hellwig3f3942a2018-05-15 15:57:23 +0200297 entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
298 acpi_device_dir(device), acpi_button_state_seq_show,
299 device);
Zhang Rui912b7422011-03-23 10:21:40 +0800300 if (!entry) {
301 ret = -ENODEV;
302 goto remove_dev_dir;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400303 }
304
Zhang Rui912b7422011-03-23 10:21:40 +0800305done:
306 return ret;
307
308remove_dev_dir:
309 remove_proc_entry(acpi_device_bid(device),
310 acpi_lid_dir);
311 acpi_device_dir(device) = NULL;
312remove_lid_dir:
313 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200314 acpi_lid_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800315remove_button_dir:
316 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200317 acpi_button_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800318 goto done;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400319}
320
Len Brown4be44fc2005-08-05 00:44:28 -0400321static int acpi_button_remove_fs(struct acpi_device *device)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400322{
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500323 struct acpi_button *button = acpi_driver_data(device);
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400324
Zhang Rui912b7422011-03-23 10:21:40 +0800325 if (button->type != ACPI_BUTTON_TYPE_LID)
326 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400327
Zhang Rui912b7422011-03-23 10:21:40 +0800328 remove_proc_entry(ACPI_BUTTON_FILE_STATE,
329 acpi_device_dir(device));
330 remove_proc_entry(acpi_device_bid(device),
331 acpi_lid_dir);
332 acpi_device_dir(device) = NULL;
333 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200334 acpi_lid_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800335 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200336 acpi_button_dir = NULL;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400337
Patrick Mocheld550d982006-06-27 00:41:40 -0400338 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400339}
340
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341/* --------------------------------------------------------------------------
342 Driver Interface
343 -------------------------------------------------------------------------- */
Jesse Barnes7e127152009-09-10 15:28:02 -0700344int acpi_lid_notifier_register(struct notifier_block *nb)
345{
346 return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
347}
348EXPORT_SYMBOL(acpi_lid_notifier_register);
349
350int acpi_lid_notifier_unregister(struct notifier_block *nb)
351{
352 return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
353}
354EXPORT_SYMBOL(acpi_lid_notifier_unregister);
355
356int acpi_lid_open(void)
357{
Jesse Barnes2c907b72009-10-07 14:39:46 -0700358 if (!lid_device)
359 return -ENODEV;
360
Lv Zhengee7e2262016-06-01 18:10:42 +0800361 return acpi_lid_evaluate_state(lid_device);
Jesse Barnes7e127152009-09-10 15:28:02 -0700362}
363EXPORT_SYMBOL(acpi_lid_open);
364
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700365static int acpi_lid_update_state(struct acpi_device *device,
366 bool signal_wakeup)
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400367{
Lv Zhengee7e2262016-06-01 18:10:42 +0800368 int state;
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400369
Lv Zhengee7e2262016-06-01 18:10:42 +0800370 state = acpi_lid_evaluate_state(device);
371 if (state < 0)
372 return state;
Bjorn Helgaas50a4da82009-04-08 15:39:38 +0000373
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700374 if (state && signal_wakeup)
375 acpi_pm_wakeup_event(&device->dev);
376
Lv Zhengee7e2262016-06-01 18:10:42 +0800377 return acpi_lid_notify_state(device, state);
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400378}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Lv Zheng3540c322016-06-01 18:10:48 +0800380static void acpi_lid_initialize_state(struct acpi_device *device)
381{
382 switch (lid_init_state) {
383 case ACPI_BUTTON_LID_INIT_OPEN:
384 (void)acpi_lid_notify_state(device, 1);
385 break;
Lv Zhengf369fdf2017-05-09 15:02:22 +0800386 case ACPI_BUTTON_LID_INIT_METHOD:
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700387 (void)acpi_lid_update_state(device, false);
Lv Zhengf369fdf2017-05-09 15:02:22 +0800388 break;
Lv Zheng3540c322016-06-01 18:10:48 +0800389 case ACPI_BUTTON_LID_INIT_IGNORE:
390 default:
391 break;
392 }
393}
394
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000395static void acpi_button_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000397 struct acpi_button *button = acpi_driver_data(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500398 struct input_dev *input;
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200399 int users;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 switch (event) {
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000402 case ACPI_FIXED_HARDWARE_EVENT:
403 event = ACPI_BUTTON_NOTIFY_STATUS;
404 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 case ACPI_BUTTON_NOTIFY_STATUS:
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500406 input = button->input;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500407 if (button->type == ACPI_BUTTON_TYPE_LID) {
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200408 mutex_lock(&button->input->mutex);
409 users = button->input->users;
410 mutex_unlock(&button->input->mutex);
411 if (users)
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700412 acpi_lid_update_state(device, true);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500413 } else {
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200414 int keycode;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500415
Rafael J. Wysocki33e4f802017-06-12 22:56:34 +0200416 acpi_pm_wakeup_event(&device->dev);
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200417 if (button->suspended)
418 break;
419
420 keycode = test_bit(KEY_SLEEP, input->keybit) ?
421 KEY_SLEEP : KEY_POWER;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500422 input_report_key(input, keycode, 1);
423 input_sync(input);
424 input_report_key(input, keycode, 0);
Guillem Joverdf316e92008-10-24 00:28:33 +0300425 input_sync(input);
Rafael J. Wysocki1f835112011-01-06 23:36:01 +0100426
Lan Tianyu0bf63682014-03-15 13:37:13 -0400427 acpi_bus_generate_netlink_event(
428 device->pnp.device_class,
429 dev_name(&device->dev),
430 event, ++button->pushed);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500431 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 break;
433 default:
434 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400435 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 break;
437 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438}
439
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200440#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200441static int acpi_button_suspend(struct device *dev)
442{
443 struct acpi_device *device = to_acpi_device(dev);
444 struct acpi_button *button = acpi_driver_data(device);
445
446 button->suspended = true;
447 return 0;
448}
449
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200450static int acpi_button_resume(struct device *dev)
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400451{
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200452 struct acpi_device *device = to_acpi_device(dev);
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000453 struct acpi_button *button = acpi_driver_data(device);
Bjorn Helgaas50a4da82009-04-08 15:39:38 +0000454
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200455 button->suspended = false;
Zhang Rui13e96212019-04-02 21:38:32 +0800456 if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) {
457 button->last_state = !!acpi_lid_evaluate_state(device);
458 button->last_time = ktime_get();
Lv Zheng3540c322016-06-01 18:10:48 +0800459 acpi_lid_initialize_state(device);
Zhang Rui13e96212019-04-02 21:38:32 +0800460 }
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400461 return 0;
462}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200463#endif
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400464
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200465static int acpi_lid_input_open(struct input_dev *input)
466{
467 struct acpi_device *device = input_get_drvdata(input);
468 struct acpi_button *button = acpi_driver_data(device);
469
470 button->last_state = !!acpi_lid_evaluate_state(device);
471 button->last_time = ktime_get();
472 acpi_lid_initialize_state(device);
473
474 return 0;
475}
476
Len Brown4be44fc2005-08-05 00:44:28 -0400477static int acpi_button_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500479 struct acpi_button *button;
480 struct input_dev *input;
Thomas Renninger620e1122010-10-01 10:54:00 +0200481 const char *hid = acpi_device_hid(device);
482 char *name, *class;
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000483 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Hans de Goede593681e2019-10-26 22:24:32 +0200485 if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
486 (dmi_check_system(lid_blacklst) ||
487 lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED))
Hans de Goede9e811e12017-11-22 16:06:12 +0100488 return -ENODEV;
489
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500490 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 if (!button)
Patrick Mocheld550d982006-06-27 00:41:40 -0400492 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700494 device->driver_data = button;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500496 button->input = input = input_allocate_device();
497 if (!input) {
498 error = -ENOMEM;
499 goto err_free_button;
500 }
501
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000502 name = acpi_device_name(device);
503 class = acpi_device_class(device);
504
Bjorn Helgaasd68b5972009-04-08 15:40:04 +0000505 if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
506 !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 button->type = ACPI_BUTTON_TYPE_POWER;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000508 strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
509 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
Bjorn Helgaasd68b5972009-04-08 15:40:04 +0000511 } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
512 !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 button->type = ACPI_BUTTON_TYPE_SLEEP;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000514 strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
515 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000517 } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 button->type = ACPI_BUTTON_TYPE_LID;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000519 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
520 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200522 input->open = acpi_lid_input_open;
Len Brown4be44fc2005-08-05 00:44:28 -0400523 } else {
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000524 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500525 error = -ENODEV;
526 goto err_free_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 }
528
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500529 error = acpi_button_add_fs(device);
530 if (error)
531 goto err_free_input;
532
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000533 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500534
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000535 input->name = name;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500536 input->phys = button->phys;
537 input->id.bustype = BUS_HOST;
538 input->id.product = button->type;
Andrey Borzenkov3b34e522008-03-04 15:06:35 -0800539 input->dev.parent = &device->dev;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 switch (button->type) {
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500542 case ACPI_BUTTON_TYPE_POWER:
Lan Tianyu763f5272013-09-12 03:32:03 -0400543 input_set_capability(input, EV_KEY, KEY_POWER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 break;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500545
546 case ACPI_BUTTON_TYPE_SLEEP:
Lan Tianyu763f5272013-09-12 03:32:03 -0400547 input_set_capability(input, EV_KEY, KEY_SLEEP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 break;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500549
550 case ACPI_BUTTON_TYPE_LID:
Lan Tianyu763f5272013-09-12 03:32:03 -0400551 input_set_capability(input, EV_SW, SW_LID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 break;
553 }
554
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200555 input_set_drvdata(input, device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500556 error = input_register_device(input);
557 if (error)
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000558 goto err_remove_fs;
Jesse Barnes7e127152009-09-10 15:28:02 -0700559 if (button->type == ACPI_BUTTON_TYPE_LID) {
Jesse Barnes7e127152009-09-10 15:28:02 -0700560 /*
561 * This assumes there's only one lid device, or if there are
562 * more we only care about the last one...
563 */
564 lid_device = device;
565 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Rafael J. Wysocki33e4f802017-06-12 22:56:34 +0200567 device_init_wakeup(&device->dev, true);
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000568 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500569 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500571 err_remove_fs:
572 acpi_button_remove_fs(device);
573 err_free_input:
574 input_free_device(input);
575 err_free_button:
576 kfree(button);
577 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100580static int acpi_button_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000582 struct acpi_button *button = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Len Brown4be44fc2005-08-05 00:44:28 -0400584 acpi_button_remove_fs(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500585 input_unregister_device(button->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 kfree(button);
Patrick Mocheld550d982006-06-27 00:41:40 -0400587 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588}
589
Kees Cooke4dca7b2017-10-17 19:04:42 -0700590static int param_set_lid_init_state(const char *val,
591 const struct kernel_param *kp)
Lv Zheng3540c322016-06-01 18:10:48 +0800592{
Hans de Goede065bd4d2019-10-26 22:24:31 +0200593 int i;
Lv Zheng3540c322016-06-01 18:10:48 +0800594
Hans de Goede065bd4d2019-10-26 22:24:31 +0200595 i = sysfs_match_string(lid_init_state_str, val);
596 if (i < 0)
597 return i;
598
599 lid_init_state = i;
600 pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
601 return 0;
Lv Zheng3540c322016-06-01 18:10:48 +0800602}
603
Hans de Goede065bd4d2019-10-26 22:24:31 +0200604static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
Lv Zheng3540c322016-06-01 18:10:48 +0800605{
Hans de Goede065bd4d2019-10-26 22:24:31 +0200606 int i, c = 0;
607
608 for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
609 if (i == lid_init_state)
610 c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
611 else
612 c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
613
614 buf[c - 1] = '\n'; /* Replace the final space with a newline */
615
616 return c;
Lv Zheng3540c322016-06-01 18:10:48 +0800617}
618
619module_param_call(lid_init_state,
620 param_set_lid_init_state, param_get_lid_init_state,
621 NULL, 0644);
622MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
623
Ard Biesheuvelac1e55b2018-04-23 11:16:56 +0200624static int acpi_button_register_driver(struct acpi_driver *driver)
625{
626 /*
627 * Modules such as nouveau.ko and i915.ko have a link time dependency
628 * on acpi_lid_open(), and would therefore not be loadable on ACPI
629 * capable kernels booted in non-ACPI mode if the return value of
630 * acpi_bus_register_driver() is returned from here with ACPI disabled
631 * when this driver is built as a module.
632 */
633 if (acpi_disabled)
634 return 0;
635
636 return acpi_bus_register_driver(driver);
637}
638
639static void acpi_button_unregister_driver(struct acpi_driver *driver)
640{
641 if (!acpi_disabled)
642 acpi_bus_unregister_driver(driver);
643}
644
645module_driver(acpi_button_driver, acpi_button_register_driver,
646 acpi_button_unregister_driver);