1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * adv_swbutton.c - Software Button Interface Driver. 4 * 5 * (C) Copyright 2020 Advantech Corporation, Inc 6 * 7 */ 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/input.h> 11 #include <linux/acpi.h> 12 #include <linux/platform_device.h> 13 14 #define ACPI_BUTTON_HID_SWBTN "AHC0310" 15 16 #define ACPI_BUTTON_NOTIFY_SWBTN_RELEASE 0x86 17 #define ACPI_BUTTON_NOTIFY_SWBTN_PRESSED 0x85 18 19 struct adv_swbutton { 20 struct input_dev *input; 21 char phys[32]; 22 }; 23 24 /*------------------------------------------------------------------------- 25 * Driver Interface 26 *-------------------------------------------------------------------------- 27 */ 28 static void adv_swbutton_notify(acpi_handle handle, u32 event, void *context) 29 { 30 struct platform_device *device = context; 31 struct adv_swbutton *button = dev_get_drvdata(&device->dev); 32 33 switch (event) { 34 case ACPI_BUTTON_NOTIFY_SWBTN_RELEASE: 35 input_report_key(button->input, KEY_PROG1, 0); 36 input_sync(button->input); 37 break; 38 case ACPI_BUTTON_NOTIFY_SWBTN_PRESSED: 39 input_report_key(button->input, KEY_PROG1, 1); 40 input_sync(button->input); 41 break; 42 default: 43 dev_dbg(&device->dev, "Unsupported event [0x%x]\n", event); 44 } 45 } 46 47 static int adv_swbutton_probe(struct platform_device *device) 48 { 49 struct adv_swbutton *button; 50 struct input_dev *input; 51 acpi_handle handle = ACPI_HANDLE(&device->dev); 52 acpi_status status; 53 int error; 54 55 button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); 56 if (!button) 57 return -ENOMEM; 58 59 dev_set_drvdata(&device->dev, button); 60 61 input = devm_input_allocate_device(&device->dev); 62 if (!input) 63 return -ENOMEM; 64 65 button->input = input; 66 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", ACPI_BUTTON_HID_SWBTN); 67 68 input->name = "Advantech Software Button"; 69 input->phys = button->phys; 70 input->id.bustype = BUS_HOST; 71 input->dev.parent = &device->dev; 72 set_bit(EV_REP, input->evbit); 73 input_set_capability(input, EV_KEY, KEY_PROG1); 74 75 error = input_register_device(input); 76 if (error) 77 return error; 78 79 device_init_wakeup(&device->dev, true); 80 81 status = acpi_install_notify_handler(handle, 82 ACPI_DEVICE_NOTIFY, 83 adv_swbutton_notify, 84 device); 85 if (ACPI_FAILURE(status)) { 86 dev_err(&device->dev, "Error installing notify handler\n"); 87 return -EIO; 88 } 89 90 return 0; 91 } 92 93 static void adv_swbutton_remove(struct platform_device *device) 94 { 95 acpi_handle handle = ACPI_HANDLE(&device->dev); 96 97 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, 98 adv_swbutton_notify); 99 } 100 101 static const struct acpi_device_id button_device_ids[] = { 102 {ACPI_BUTTON_HID_SWBTN, 0}, 103 {"", 0}, 104 }; 105 MODULE_DEVICE_TABLE(acpi, button_device_ids); 106 107 static struct platform_driver adv_swbutton_driver = { 108 .driver = { 109 .name = "adv_swbutton", 110 .acpi_match_table = button_device_ids, 111 }, 112 .probe = adv_swbutton_probe, 113 .remove = adv_swbutton_remove, 114 }; 115 module_platform_driver(adv_swbutton_driver); 116 117 MODULE_AUTHOR("Andrea Ho"); 118 MODULE_DESCRIPTION("Advantech ACPI SW Button Driver"); 119 MODULE_LICENSE("GPL v2"); 120