162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  adv_swbutton.c - Software Button Interface Driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  (C) Copyright 2020 Advantech Corporation, Inc
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/input.h>
1162306a36Sopenharmony_ci#include <linux/acpi.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define ACPI_BUTTON_HID_SWBTN               "AHC0310"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define ACPI_BUTTON_NOTIFY_SWBTN_RELEASE    0x86
1762306a36Sopenharmony_ci#define ACPI_BUTTON_NOTIFY_SWBTN_PRESSED    0x85
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct adv_swbutton {
2062306a36Sopenharmony_ci	struct input_dev *input;
2162306a36Sopenharmony_ci	char phys[32];
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*-------------------------------------------------------------------------
2562306a36Sopenharmony_ci *                               Driver Interface
2662306a36Sopenharmony_ci *--------------------------------------------------------------------------
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic void adv_swbutton_notify(acpi_handle handle, u32 event, void *context)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct platform_device *device = context;
3162306a36Sopenharmony_ci	struct adv_swbutton *button = dev_get_drvdata(&device->dev);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	switch (event) {
3462306a36Sopenharmony_ci	case ACPI_BUTTON_NOTIFY_SWBTN_RELEASE:
3562306a36Sopenharmony_ci		input_report_key(button->input, KEY_PROG1, 0);
3662306a36Sopenharmony_ci		input_sync(button->input);
3762306a36Sopenharmony_ci		break;
3862306a36Sopenharmony_ci	case ACPI_BUTTON_NOTIFY_SWBTN_PRESSED:
3962306a36Sopenharmony_ci		input_report_key(button->input, KEY_PROG1, 1);
4062306a36Sopenharmony_ci		input_sync(button->input);
4162306a36Sopenharmony_ci		break;
4262306a36Sopenharmony_ci	default:
4362306a36Sopenharmony_ci		dev_dbg(&device->dev, "Unsupported event [0x%x]\n", event);
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int adv_swbutton_probe(struct platform_device *device)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct adv_swbutton *button;
5062306a36Sopenharmony_ci	struct input_dev *input;
5162306a36Sopenharmony_ci	acpi_handle handle = ACPI_HANDLE(&device->dev);
5262306a36Sopenharmony_ci	acpi_status status;
5362306a36Sopenharmony_ci	int error;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL);
5662306a36Sopenharmony_ci	if (!button)
5762306a36Sopenharmony_ci		return -ENOMEM;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	dev_set_drvdata(&device->dev, button);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	input = devm_input_allocate_device(&device->dev);
6262306a36Sopenharmony_ci	if (!input)
6362306a36Sopenharmony_ci		return -ENOMEM;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	button->input = input;
6662306a36Sopenharmony_ci	snprintf(button->phys, sizeof(button->phys), "%s/button/input0", ACPI_BUTTON_HID_SWBTN);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	input->name = "Advantech Software Button";
6962306a36Sopenharmony_ci	input->phys = button->phys;
7062306a36Sopenharmony_ci	input->id.bustype = BUS_HOST;
7162306a36Sopenharmony_ci	input->dev.parent = &device->dev;
7262306a36Sopenharmony_ci	set_bit(EV_REP, input->evbit);
7362306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_PROG1);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	error = input_register_device(input);
7662306a36Sopenharmony_ci	if (error)
7762306a36Sopenharmony_ci		return error;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	device_init_wakeup(&device->dev, true);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	status = acpi_install_notify_handler(handle,
8262306a36Sopenharmony_ci					     ACPI_DEVICE_NOTIFY,
8362306a36Sopenharmony_ci					     adv_swbutton_notify,
8462306a36Sopenharmony_ci					     device);
8562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
8662306a36Sopenharmony_ci		dev_err(&device->dev, "Error installing notify handler\n");
8762306a36Sopenharmony_ci		return -EIO;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void adv_swbutton_remove(struct platform_device *device)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	acpi_handle handle = ACPI_HANDLE(&device->dev);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY,
9862306a36Sopenharmony_ci				   adv_swbutton_notify);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct acpi_device_id button_device_ids[] = {
10262306a36Sopenharmony_ci	{ACPI_BUTTON_HID_SWBTN, 0},
10362306a36Sopenharmony_ci	{"", 0},
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, button_device_ids);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic struct platform_driver adv_swbutton_driver = {
10862306a36Sopenharmony_ci	.driver = {
10962306a36Sopenharmony_ci		.name = "adv_swbutton",
11062306a36Sopenharmony_ci		.acpi_match_table = button_device_ids,
11162306a36Sopenharmony_ci	},
11262306a36Sopenharmony_ci	.probe = adv_swbutton_probe,
11362306a36Sopenharmony_ci	.remove_new = adv_swbutton_remove,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_cimodule_platform_driver(adv_swbutton_driver);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Ho");
11862306a36Sopenharmony_ciMODULE_DESCRIPTION("Advantech ACPI SW Button Driver");
11962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
120