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; 52 acpi_status status; 53 int error; 54 55 handle = ACPI_HANDLE(&device->dev); 56 if (!handle) 57 return -ENODEV; 58 59 button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); 60 if (!button) 61 return -ENOMEM; 62 63 dev_set_drvdata(&device->dev, button); 64 65 input = devm_input_allocate_device(&device->dev); 66 if (!input) 67 return -ENOMEM; 68 69 button->input = input; 70 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", ACPI_BUTTON_HID_SWBTN); 71 72 input->name = "Advantech Software Button"; 73 input->phys = button->phys; 74 input->id.bustype = BUS_HOST; 75 input->dev.parent = &device->dev; 76 set_bit(EV_REP, input->evbit); 77 input_set_capability(input, EV_KEY, KEY_PROG1); 78 79 error = input_register_device(input); 80 if (error) 81 return error; 82 83 device_init_wakeup(&device->dev, true); 84 85 status = acpi_install_notify_handler(handle, 86 ACPI_DEVICE_NOTIFY, 87 adv_swbutton_notify, 88 device); 89 if (ACPI_FAILURE(status)) { 90 dev_err(&device->dev, "Error installing notify handler\n"); 91 return -EIO; 92 } 93 94 return 0; 95 } 96 97 static void adv_swbutton_remove(struct platform_device *device) 98 { 99 acpi_handle handle = ACPI_HANDLE(&device->dev); 100 101 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, 102 adv_swbutton_notify); 103 } 104 105 static const struct acpi_device_id button_device_ids[] = { 106 {ACPI_BUTTON_HID_SWBTN, 0}, 107 {"", 0}, 108 }; 109 MODULE_DEVICE_TABLE(acpi, button_device_ids); 110 111 static struct platform_driver adv_swbutton_driver = { 112 .driver = { 113 .name = "adv_swbutton", 114 .acpi_match_table = button_device_ids, 115 }, 116 .probe = adv_swbutton_probe, 117 .remove = adv_swbutton_remove, 118 }; 119 module_platform_driver(adv_swbutton_driver); 120 121 MODULE_AUTHOR("Andrea Ho"); 122 MODULE_DESCRIPTION("Advantech ACPI SW Button Driver"); 123 MODULE_LICENSE("GPL v2"); 124