blob: d83b15bae515bb8c02b1bb4072da73efcd582204 [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 Goeded7cd0822019-10-26 22:24:33 +020078/* Please keep this list sorted alphabetically by vendor and model */
79static const struct dmi_system_id dmi_lid_quirks[] = {
Hans de Goede9e811e12017-11-22 16:06:12 +010080 {
Hans de Goeded7cd0822019-10-26 22:24:33 +020081 /* GP-electronic T701, _LID method points to a floating GPIO */
Hans de Goede9e811e12017-11-22 16:06:12 +010082 .matches = {
83 DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
84 DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
85 DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
86 },
Hans de Goeded7cd0822019-10-26 22:24:33 +020087 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
Hans de Goede9e811e12017-11-22 16:06:12 +010088 },
89 {}
90};
91
Len Brown4be44fc2005-08-05 00:44:28 -040092static int acpi_button_add(struct acpi_device *device);
Rafael J. Wysocki51fac832013-01-24 00:24:48 +010093static int acpi_button_remove(struct acpi_device *device);
Bjorn Helgaas373cfc32009-03-30 17:48:18 +000094static void acpi_button_notify(struct acpi_device *device, u32 event);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
Rafael J. Wysocki90692402012-08-09 23:00:02 +020096#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +020097static int acpi_button_suspend(struct device *dev);
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +020098static int acpi_button_resume(struct device *dev);
Shuah Khan2de9fd12014-02-12 20:19:07 -070099#else
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200100#define acpi_button_suspend NULL
Shuah Khan2de9fd12014-02-12 20:19:07 -0700101#define acpi_button_resume NULL
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200102#endif
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200103static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105static struct acpi_driver acpi_button_driver = {
Len Brownc2b67052007-02-12 23:33:40 -0500106 .name = "button",
Len Brown4be44fc2005-08-05 00:44:28 -0400107 .class = ACPI_BUTTON_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +0200108 .ids = button_device_ids,
Len Brown4be44fc2005-08-05 00:44:28 -0400109 .ops = {
110 .add = acpi_button_add,
111 .remove = acpi_button_remove,
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000112 .notify = acpi_button_notify,
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500113 },
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200114 .drv.pm = &acpi_button_pm,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct acpi_button {
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500118 unsigned int type;
119 struct input_dev *input;
120 char phys[32]; /* for input device */
Len Brown4be44fc2005-08-05 00:44:28 -0400121 unsigned long pushed;
Lv Zhengdfa46c52016-08-17 16:22:58 +0800122 int last_state;
123 ktime_t last_time;
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200124 bool suspended;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
Jesse Barnes7e127152009-09-10 15:28:02 -0700127static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
128static struct acpi_device *lid_device;
Hans de Goeded7cd0822019-10-26 22:24:33 +0200129static long lid_init_state = -1;
Jesse Barnes7e127152009-09-10 15:28:02 -0700130
Lv Zhengdfa46c52016-08-17 16:22:58 +0800131static unsigned long lid_report_interval __read_mostly = 500;
132module_param(lid_report_interval, ulong, 0644);
133MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
134
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400135/* --------------------------------------------------------------------------
136 FS Interface (/proc)
137 -------------------------------------------------------------------------- */
138
Len Brown4be44fc2005-08-05 00:44:28 -0400139static struct proc_dir_entry *acpi_button_dir;
Zhang Rui912b7422011-03-23 10:21:40 +0800140static struct proc_dir_entry *acpi_lid_dir;
Len Brown4be44fc2005-08-05 00:44:28 -0400141
Lv Zhengee7e2262016-06-01 18:10:42 +0800142static int acpi_lid_evaluate_state(struct acpi_device *device)
143{
144 unsigned long long lid_state;
145 acpi_status status;
146
147 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
148 if (ACPI_FAILURE(status))
149 return -ENODEV;
150
151 return lid_state ? 1 : 0;
152}
153
154static int acpi_lid_notify_state(struct acpi_device *device, int state)
155{
156 struct acpi_button *button = acpi_driver_data(device);
157 int ret;
Lv Zhengdfa46c52016-08-17 16:22:58 +0800158 ktime_t next_report;
159 bool do_update;
Lv Zhengee7e2262016-06-01 18:10:42 +0800160
Lv Zhengdfa46c52016-08-17 16:22:58 +0800161 /*
162 * In lid_init_state=ignore mode, if user opens/closes lid
163 * frequently with "open" missing, and "last_time" is also updated
164 * frequently, "close" cannot be delivered to the userspace.
165 * So "last_time" is only updated after a timeout or an actual
166 * switch.
167 */
168 if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
169 button->last_state != !!state)
170 do_update = true;
171 else
172 do_update = false;
173
174 next_report = ktime_add(button->last_time,
175 ms_to_ktime(lid_report_interval));
176 if (button->last_state == !!state &&
177 ktime_after(ktime_get(), next_report)) {
178 /* Complain the buggy firmware */
179 pr_warn_once("The lid device is not compliant to SW_LID.\n");
180
181 /*
182 * Send the unreliable complement switch event:
183 *
184 * On most platforms, the lid device is reliable. However
185 * there are exceptions:
186 * 1. Platforms returning initial lid state as "close" by
187 * default after booting/resuming:
188 * https://bugzilla.kernel.org/show_bug.cgi?id=89211
189 * https://bugzilla.kernel.org/show_bug.cgi?id=106151
190 * 2. Platforms never reporting "open" events:
191 * https://bugzilla.kernel.org/show_bug.cgi?id=106941
192 * On these buggy platforms, the usage model of the ACPI
193 * lid device actually is:
194 * 1. The initial returning value of _LID may not be
195 * reliable.
196 * 2. The open event may not be reliable.
197 * 3. The close event is reliable.
198 *
199 * But SW_LID is typed as input switch event, the input
200 * layer checks if the event is redundant. Hence if the
201 * state is not switched, the userspace cannot see this
202 * platform triggered reliable event. By inserting a
203 * complement switch event, it then is guaranteed that the
204 * platform triggered reliable one can always be seen by
205 * the userspace.
206 */
207 if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
208 do_update = true;
209 /*
210 * Do generate complement switch event for "close"
211 * as "close" is reliable and wrong "open" won't
212 * trigger unexpected behaviors.
213 * Do not generate complement switch event for
214 * "open" as "open" is not reliable and wrong
215 * "close" will trigger unexpected behaviors.
216 */
217 if (!state) {
218 input_report_switch(button->input,
219 SW_LID, state);
220 input_sync(button->input);
221 }
222 }
223 }
224 /* Send the platform triggered reliable event */
225 if (do_update) {
Hans de Goedeae35d652017-11-22 16:06:11 +0100226 acpi_handle_debug(device->handle, "ACPI LID %s\n",
227 state ? "open" : "closed");
Lv Zhengdfa46c52016-08-17 16:22:58 +0800228 input_report_switch(button->input, SW_LID, !state);
229 input_sync(button->input);
230 button->last_state = !!state;
231 button->last_time = ktime_get();
232 }
Lv Zhengee7e2262016-06-01 18:10:42 +0800233
Lv Zhengee7e2262016-06-01 18:10:42 +0800234 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
235 if (ret == NOTIFY_DONE)
236 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
237 device);
238 if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
239 /*
240 * It is also regarded as success if the notifier_chain
241 * returns NOTIFY_OK or NOTIFY_DONE.
242 */
243 ret = 0;
244 }
245 return ret;
246}
247
Randy Dunlap2c4c2a72018-07-07 08:25:01 -0700248static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
249 void *offset)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400250{
Bjorn Helgaas106c19e2009-04-08 15:39:59 +0000251 struct acpi_device *device = seq->private;
Lv Zhengee7e2262016-06-01 18:10:42 +0800252 int state;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400253
Lv Zhengee7e2262016-06-01 18:10:42 +0800254 state = acpi_lid_evaluate_state(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500255 seq_printf(seq, "state: %s\n",
Lv Zhengee7e2262016-06-01 18:10:42 +0800256 state < 0 ? "unsupported" : (state ? "open" : "closed"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400257 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400258}
259
Len Brown4be44fc2005-08-05 00:44:28 -0400260static int acpi_button_add_fs(struct acpi_device *device)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400261{
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000262 struct acpi_button *button = acpi_driver_data(device);
Len Brown4be44fc2005-08-05 00:44:28 -0400263 struct proc_dir_entry *entry = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800264 int ret = 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400265
Zhang Rui912b7422011-03-23 10:21:40 +0800266 /* procfs I/F for ACPI lid device only */
267 if (button->type != ACPI_BUTTON_TYPE_LID)
268 return 0;
269
270 if (acpi_button_dir || acpi_lid_dir) {
271 printk(KERN_ERR PREFIX "More than one Lid device found!\n");
272 return -EEXIST;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400273 }
274
Zhang Rui912b7422011-03-23 10:21:40 +0800275 /* create /proc/acpi/button */
276 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
277 if (!acpi_button_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -0400278 return -ENODEV;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400279
Zhang Rui912b7422011-03-23 10:21:40 +0800280 /* create /proc/acpi/button/lid */
281 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
282 if (!acpi_lid_dir) {
283 ret = -ENODEV;
284 goto remove_button_dir;
285 }
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400286
Zhang Rui912b7422011-03-23 10:21:40 +0800287 /* create /proc/acpi/button/lid/LID/ */
288 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
289 if (!acpi_device_dir(device)) {
290 ret = -ENODEV;
291 goto remove_lid_dir;
292 }
293
294 /* create /proc/acpi/button/lid/LID/state */
Christoph Hellwig3f3942a2018-05-15 15:57:23 +0200295 entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
296 acpi_device_dir(device), acpi_button_state_seq_show,
297 device);
Zhang Rui912b7422011-03-23 10:21:40 +0800298 if (!entry) {
299 ret = -ENODEV;
300 goto remove_dev_dir;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400301 }
302
Zhang Rui912b7422011-03-23 10:21:40 +0800303done:
304 return ret;
305
306remove_dev_dir:
307 remove_proc_entry(acpi_device_bid(device),
308 acpi_lid_dir);
309 acpi_device_dir(device) = NULL;
310remove_lid_dir:
311 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200312 acpi_lid_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800313remove_button_dir:
314 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200315 acpi_button_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800316 goto done;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400317}
318
Len Brown4be44fc2005-08-05 00:44:28 -0400319static int acpi_button_remove_fs(struct acpi_device *device)
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400320{
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500321 struct acpi_button *button = acpi_driver_data(device);
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400322
Zhang Rui912b7422011-03-23 10:21:40 +0800323 if (button->type != ACPI_BUTTON_TYPE_LID)
324 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400325
Zhang Rui912b7422011-03-23 10:21:40 +0800326 remove_proc_entry(ACPI_BUTTON_FILE_STATE,
327 acpi_device_dir(device));
328 remove_proc_entry(acpi_device_bid(device),
329 acpi_lid_dir);
330 acpi_device_dir(device) = NULL;
331 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200332 acpi_lid_dir = NULL;
Zhang Rui912b7422011-03-23 10:21:40 +0800333 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
Benjamin Tissoirese370cc82016-07-29 17:08:41 +0200334 acpi_button_dir = NULL;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400335
Patrick Mocheld550d982006-06-27 00:41:40 -0400336 return 0;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400337}
338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339/* --------------------------------------------------------------------------
340 Driver Interface
341 -------------------------------------------------------------------------- */
Jesse Barnes7e127152009-09-10 15:28:02 -0700342int acpi_lid_notifier_register(struct notifier_block *nb)
343{
344 return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
345}
346EXPORT_SYMBOL(acpi_lid_notifier_register);
347
348int acpi_lid_notifier_unregister(struct notifier_block *nb)
349{
350 return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
351}
352EXPORT_SYMBOL(acpi_lid_notifier_unregister);
353
354int acpi_lid_open(void)
355{
Jesse Barnes2c907b72009-10-07 14:39:46 -0700356 if (!lid_device)
357 return -ENODEV;
358
Lv Zhengee7e2262016-06-01 18:10:42 +0800359 return acpi_lid_evaluate_state(lid_device);
Jesse Barnes7e127152009-09-10 15:28:02 -0700360}
361EXPORT_SYMBOL(acpi_lid_open);
362
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700363static int acpi_lid_update_state(struct acpi_device *device,
364 bool signal_wakeup)
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400365{
Lv Zhengee7e2262016-06-01 18:10:42 +0800366 int state;
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400367
Lv Zhengee7e2262016-06-01 18:10:42 +0800368 state = acpi_lid_evaluate_state(device);
369 if (state < 0)
370 return state;
Bjorn Helgaas50a4da82009-04-08 15:39:38 +0000371
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700372 if (state && signal_wakeup)
373 acpi_pm_wakeup_event(&device->dev);
374
Lv Zhengee7e2262016-06-01 18:10:42 +0800375 return acpi_lid_notify_state(device, state);
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400376}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Lv Zheng3540c322016-06-01 18:10:48 +0800378static void acpi_lid_initialize_state(struct acpi_device *device)
379{
380 switch (lid_init_state) {
381 case ACPI_BUTTON_LID_INIT_OPEN:
382 (void)acpi_lid_notify_state(device, 1);
383 break;
Lv Zhengf369fdf2017-05-09 15:02:22 +0800384 case ACPI_BUTTON_LID_INIT_METHOD:
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700385 (void)acpi_lid_update_state(device, false);
Lv Zhengf369fdf2017-05-09 15:02:22 +0800386 break;
Lv Zheng3540c322016-06-01 18:10:48 +0800387 case ACPI_BUTTON_LID_INIT_IGNORE:
388 default:
389 break;
390 }
391}
392
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000393static void acpi_button_notify(struct acpi_device *device, u32 event)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394{
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000395 struct acpi_button *button = acpi_driver_data(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500396 struct input_dev *input;
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200397 int users;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 switch (event) {
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000400 case ACPI_FIXED_HARDWARE_EVENT:
401 event = ACPI_BUTTON_NOTIFY_STATUS;
402 /* fall through */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 case ACPI_BUTTON_NOTIFY_STATUS:
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500404 input = button->input;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500405 if (button->type == ACPI_BUTTON_TYPE_LID) {
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200406 mutex_lock(&button->input->mutex);
407 users = button->input->users;
408 mutex_unlock(&button->input->mutex);
409 if (users)
Ravi Chandra Sadineni7c058c72018-06-27 10:55:02 -0700410 acpi_lid_update_state(device, true);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500411 } else {
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200412 int keycode;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500413
Rafael J. Wysocki33e4f802017-06-12 22:56:34 +0200414 acpi_pm_wakeup_event(&device->dev);
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200415 if (button->suspended)
416 break;
417
418 keycode = test_bit(KEY_SLEEP, input->keybit) ?
419 KEY_SLEEP : KEY_POWER;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500420 input_report_key(input, keycode, 1);
421 input_sync(input);
422 input_report_key(input, keycode, 0);
Guillem Joverdf316e92008-10-24 00:28:33 +0300423 input_sync(input);
Rafael J. Wysocki1f835112011-01-06 23:36:01 +0100424
Lan Tianyu0bf63682014-03-15 13:37:13 -0400425 acpi_bus_generate_netlink_event(
426 device->pnp.device_class,
427 dev_name(&device->dev),
428 event, ++button->pushed);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500429 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 break;
431 default:
432 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400433 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 break;
435 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436}
437
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200438#ifdef CONFIG_PM_SLEEP
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200439static int acpi_button_suspend(struct device *dev)
440{
441 struct acpi_device *device = to_acpi_device(dev);
442 struct acpi_button *button = acpi_driver_data(device);
443
444 button->suspended = true;
445 return 0;
446}
447
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200448static int acpi_button_resume(struct device *dev)
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400449{
Rafael J. Wysocki1be532d2012-06-27 23:26:51 +0200450 struct acpi_device *device = to_acpi_device(dev);
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000451 struct acpi_button *button = acpi_driver_data(device);
Bjorn Helgaas50a4da82009-04-08 15:39:38 +0000452
Rafael J. Wysockie71eeb22014-07-23 00:59:04 +0200453 button->suspended = false;
Zhang Rui13e96212019-04-02 21:38:32 +0800454 if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) {
455 button->last_state = !!acpi_lid_evaluate_state(device);
456 button->last_time = ktime_get();
Lv Zheng3540c322016-06-01 18:10:48 +0800457 acpi_lid_initialize_state(device);
Zhang Rui13e96212019-04-02 21:38:32 +0800458 }
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400459 return 0;
460}
Rafael J. Wysocki90692402012-08-09 23:00:02 +0200461#endif
Alexey Starikovskiy23de5d92007-10-22 14:18:18 +0400462
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200463static int acpi_lid_input_open(struct input_dev *input)
464{
465 struct acpi_device *device = input_get_drvdata(input);
466 struct acpi_button *button = acpi_driver_data(device);
467
468 button->last_state = !!acpi_lid_evaluate_state(device);
469 button->last_time = ktime_get();
470 acpi_lid_initialize_state(device);
471
472 return 0;
473}
474
Len Brown4be44fc2005-08-05 00:44:28 -0400475static int acpi_button_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500477 struct acpi_button *button;
478 struct input_dev *input;
Thomas Renninger620e1122010-10-01 10:54:00 +0200479 const char *hid = acpi_device_hid(device);
480 char *name, *class;
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000481 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Hans de Goede593681e2019-10-26 22:24:32 +0200483 if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
Hans de Goeded7cd0822019-10-26 22:24:33 +0200484 lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
Hans de Goede9e811e12017-11-22 16:06:12 +0100485 return -ENODEV;
486
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500487 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 if (!button)
Patrick Mocheld550d982006-06-27 00:41:40 -0400489 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Pavel Machekdb89b4f2008-09-22 14:37:34 -0700491 device->driver_data = button;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500493 button->input = input = input_allocate_device();
494 if (!input) {
495 error = -ENOMEM;
496 goto err_free_button;
497 }
498
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000499 name = acpi_device_name(device);
500 class = acpi_device_class(device);
501
Bjorn Helgaasd68b5972009-04-08 15:40:04 +0000502 if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
503 !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 button->type = ACPI_BUTTON_TYPE_POWER;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000505 strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
506 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
Bjorn Helgaasd68b5972009-04-08 15:40:04 +0000508 } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
509 !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 button->type = ACPI_BUTTON_TYPE_SLEEP;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000511 strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
512 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000514 } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 button->type = ACPI_BUTTON_TYPE_LID;
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000516 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
517 sprintf(class, "%s/%s",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200519 input->open = acpi_lid_input_open;
Len Brown4be44fc2005-08-05 00:44:28 -0400520 } else {
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000521 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500522 error = -ENODEV;
523 goto err_free_input;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 }
525
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500526 error = acpi_button_add_fs(device);
527 if (error)
528 goto err_free_input;
529
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000530 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500531
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000532 input->name = name;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500533 input->phys = button->phys;
534 input->id.bustype = BUS_HOST;
535 input->id.product = button->type;
Andrey Borzenkov3b34e522008-03-04 15:06:35 -0800536 input->dev.parent = &device->dev;
Alexey Starikovskiyb34a8032005-08-03 17:55:21 -0400537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 switch (button->type) {
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500539 case ACPI_BUTTON_TYPE_POWER:
Lan Tianyu763f5272013-09-12 03:32:03 -0400540 input_set_capability(input, EV_KEY, KEY_POWER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 break;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500542
543 case ACPI_BUTTON_TYPE_SLEEP:
Lan Tianyu763f5272013-09-12 03:32:03 -0400544 input_set_capability(input, EV_KEY, KEY_SLEEP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 break;
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500546
547 case ACPI_BUTTON_TYPE_LID:
Lan Tianyu763f5272013-09-12 03:32:03 -0400548 input_set_capability(input, EV_SW, SW_LID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 break;
550 }
551
Hans de Goede84d3f6b2017-09-11 16:07:06 +0200552 input_set_drvdata(input, device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500553 error = input_register_device(input);
554 if (error)
Bjorn Helgaas373cfc32009-03-30 17:48:18 +0000555 goto err_remove_fs;
Jesse Barnes7e127152009-09-10 15:28:02 -0700556 if (button->type == ACPI_BUTTON_TYPE_LID) {
Jesse Barnes7e127152009-09-10 15:28:02 -0700557 /*
558 * This assumes there's only one lid device, or if there are
559 * more we only care about the last one...
560 */
561 lid_device = device;
562 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Rafael J. Wysocki33e4f802017-06-12 22:56:34 +0200564 device_init_wakeup(&device->dev, true);
Bjorn Helgaasbf04a772009-04-08 15:39:54 +0000565 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500566 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500568 err_remove_fs:
569 acpi_button_remove_fs(device);
570 err_free_input:
571 input_free_device(input);
572 err_free_button:
573 kfree(button);
574 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
Rafael J. Wysocki51fac832013-01-24 00:24:48 +0100577static int acpi_button_remove(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
Bjorn Helgaas1bce8112009-04-08 15:39:49 +0000579 struct acpi_button *button = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Len Brown4be44fc2005-08-05 00:44:28 -0400581 acpi_button_remove_fs(device);
Dmitry Torokhovc0968f02006-11-09 00:40:13 -0500582 input_unregister_device(button->input);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 kfree(button);
Patrick Mocheld550d982006-06-27 00:41:40 -0400584 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
Kees Cooke4dca7b2017-10-17 19:04:42 -0700587static int param_set_lid_init_state(const char *val,
588 const struct kernel_param *kp)
Lv Zheng3540c322016-06-01 18:10:48 +0800589{
Hans de Goede065bd4d2019-10-26 22:24:31 +0200590 int i;
Lv Zheng3540c322016-06-01 18:10:48 +0800591
Hans de Goede065bd4d2019-10-26 22:24:31 +0200592 i = sysfs_match_string(lid_init_state_str, val);
593 if (i < 0)
594 return i;
595
596 lid_init_state = i;
597 pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
598 return 0;
Lv Zheng3540c322016-06-01 18:10:48 +0800599}
600
Hans de Goede065bd4d2019-10-26 22:24:31 +0200601static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
Lv Zheng3540c322016-06-01 18:10:48 +0800602{
Hans de Goede065bd4d2019-10-26 22:24:31 +0200603 int i, c = 0;
604
605 for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
606 if (i == lid_init_state)
607 c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
608 else
609 c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
610
611 buf[c - 1] = '\n'; /* Replace the final space with a newline */
612
613 return c;
Lv Zheng3540c322016-06-01 18:10:48 +0800614}
615
616module_param_call(lid_init_state,
617 param_set_lid_init_state, param_get_lid_init_state,
618 NULL, 0644);
619MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
620
Ard Biesheuvelac1e55b2018-04-23 11:16:56 +0200621static int acpi_button_register_driver(struct acpi_driver *driver)
622{
Hans de Goeded7cd0822019-10-26 22:24:33 +0200623 const struct dmi_system_id *dmi_id;
624
625 if (lid_init_state == -1) {
626 dmi_id = dmi_first_match(dmi_lid_quirks);
627 if (dmi_id)
628 lid_init_state = (long)dmi_id->driver_data;
629 else
630 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
631 }
632
Ard Biesheuvelac1e55b2018-04-23 11:16:56 +0200633 /*
634 * Modules such as nouveau.ko and i915.ko have a link time dependency
635 * on acpi_lid_open(), and would therefore not be loadable on ACPI
636 * capable kernels booted in non-ACPI mode if the return value of
637 * acpi_bus_register_driver() is returned from here with ACPI disabled
638 * when this driver is built as a module.
639 */
640 if (acpi_disabled)
641 return 0;
642
643 return acpi_bus_register_driver(driver);
644}
645
646static void acpi_button_unregister_driver(struct acpi_driver *driver)
647{
648 if (!acpi_disabled)
649 acpi_bus_unregister_driver(driver);
650}
651
652module_driver(acpi_button_driver, acpi_button_register_driver,
653 acpi_button_unregister_driver);