18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/acpi.h>
118c2ecf20Sopenharmony_ci#include <linux/input.h>
128c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/leds.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define LED_DEVICE(_name, max) struct led_classdev _name = { \
208c2ecf20Sopenharmony_ci	.name           = __stringify(_name),   \
218c2ecf20Sopenharmony_ci	.max_brightness = max,                  \
228c2ecf20Sopenharmony_ci	.brightness_set = _name##_set,          \
238c2ecf20Sopenharmony_ci	.brightness_get = _name##_get,          \
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matan Ziv-Av");
278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LG WMI Hotkey Driver");
288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID0	"E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
318c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID1	"023B133E-49D1-4E10-B313-698220140DC2"
328c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID2	"37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
338c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID3	"911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
348c2ecf20Sopenharmony_ci#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
358c2ecf20Sopenharmony_ci#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
368c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID  WMI_EVENT_GUID0
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define WMAB_METHOD     "\\XINI.WMAB"
398c2ecf20Sopenharmony_ci#define WMBB_METHOD     "\\XINI.WMBB"
408c2ecf20Sopenharmony_ci#define SB_GGOV_METHOD  "\\_SB.GGOV"
418c2ecf20Sopenharmony_ci#define GOV_TLED        0x2020008
428c2ecf20Sopenharmony_ci#define WM_GET          1
438c2ecf20Sopenharmony_ci#define WM_SET          2
448c2ecf20Sopenharmony_ci#define WM_KEY_LIGHT    0x400
458c2ecf20Sopenharmony_ci#define WM_TLED         0x404
468c2ecf20Sopenharmony_ci#define WM_FN_LOCK      0x407
478c2ecf20Sopenharmony_ci#define WM_BATT_LIMIT   0x61
488c2ecf20Sopenharmony_ci#define WM_READER_MODE  0xBF
498c2ecf20Sopenharmony_ci#define WM_FAN_MODE	0x33
508c2ecf20Sopenharmony_ci#define WMBB_USB_CHARGE 0x10B
518c2ecf20Sopenharmony_ci#define WMBB_BATT_LIMIT 0x10C
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define PLATFORM_NAME   "lg-laptop"
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
568c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
578c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
588c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
598c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
608c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
618c2ecf20Sopenharmony_ciMODULE_ALIAS("acpi*:LGEX0815:*");
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic struct platform_device *pf_device;
648c2ecf20Sopenharmony_cistatic struct input_dev *wmi_input_dev;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic u32 inited;
678c2ecf20Sopenharmony_ci#define INIT_INPUT_WMI_0        0x01
688c2ecf20Sopenharmony_ci#define INIT_INPUT_WMI_2        0x02
698c2ecf20Sopenharmony_ci#define INIT_INPUT_ACPI         0x04
708c2ecf20Sopenharmony_ci#define INIT_SPARSE_KEYMAP      0x80
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const struct key_entry wmi_keymap[] = {
738c2ecf20Sopenharmony_ci	{KE_KEY, 0x70, {KEY_F15} },	 /* LG control panel (F1) */
748c2ecf20Sopenharmony_ci	{KE_KEY, 0x74, {KEY_F13} },	 /* Touchpad toggle (F5) */
758c2ecf20Sopenharmony_ci	{KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
768c2ecf20Sopenharmony_ci	{KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
778c2ecf20Sopenharmony_ci					  * this key both sends an event and
788c2ecf20Sopenharmony_ci					  * changes backlight level.
798c2ecf20Sopenharmony_ci					  */
808c2ecf20Sopenharmony_ci	{KE_KEY, 0x80, {KEY_RFKILL} },
818c2ecf20Sopenharmony_ci	{KE_END, 0}
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int ggov(u32 arg0)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	union acpi_object args[1];
878c2ecf20Sopenharmony_ci	union acpi_object *r;
888c2ecf20Sopenharmony_ci	acpi_status status;
898c2ecf20Sopenharmony_ci	acpi_handle handle;
908c2ecf20Sopenharmony_ci	struct acpi_object_list arg;
918c2ecf20Sopenharmony_ci	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
928c2ecf20Sopenharmony_ci	int res;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	args[0].type = ACPI_TYPE_INTEGER;
958c2ecf20Sopenharmony_ci	args[0].integer.value = arg0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
988c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
998c2ecf20Sopenharmony_ci		pr_err("Cannot get handle");
1008c2ecf20Sopenharmony_ci		return -ENODEV;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	arg.count = 1;
1048c2ecf20Sopenharmony_ci	arg.pointer = args;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
1078c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1088c2ecf20Sopenharmony_ci		acpi_handle_err(handle, "GGOV: call failed.\n");
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	r = buffer.pointer;
1138c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_INTEGER) {
1148c2ecf20Sopenharmony_ci		kfree(r);
1158c2ecf20Sopenharmony_ci		return -EINVAL;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	res = r->integer.value;
1198c2ecf20Sopenharmony_ci	kfree(r);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return res;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	union acpi_object args[3];
1278c2ecf20Sopenharmony_ci	acpi_status status;
1288c2ecf20Sopenharmony_ci	acpi_handle handle;
1298c2ecf20Sopenharmony_ci	struct acpi_object_list arg;
1308c2ecf20Sopenharmony_ci	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	args[0].type = ACPI_TYPE_INTEGER;
1338c2ecf20Sopenharmony_ci	args[0].integer.value = method;
1348c2ecf20Sopenharmony_ci	args[1].type = ACPI_TYPE_INTEGER;
1358c2ecf20Sopenharmony_ci	args[1].integer.value = arg1;
1368c2ecf20Sopenharmony_ci	args[2].type = ACPI_TYPE_INTEGER;
1378c2ecf20Sopenharmony_ci	args[2].integer.value = arg2;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
1408c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1418c2ecf20Sopenharmony_ci		pr_err("Cannot get handle");
1428c2ecf20Sopenharmony_ci		return NULL;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	arg.count = 3;
1468c2ecf20Sopenharmony_ci	arg.pointer = args;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
1498c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1508c2ecf20Sopenharmony_ci		acpi_handle_err(handle, "WMAB: call failed.\n");
1518c2ecf20Sopenharmony_ci		return NULL;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return buffer.pointer;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	union acpi_object args[3];
1608c2ecf20Sopenharmony_ci	acpi_status status;
1618c2ecf20Sopenharmony_ci	acpi_handle handle;
1628c2ecf20Sopenharmony_ci	struct acpi_object_list arg;
1638c2ecf20Sopenharmony_ci	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1648c2ecf20Sopenharmony_ci	u8 buf[32];
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	*(u32 *)buf = method_id;
1678c2ecf20Sopenharmony_ci	*(u32 *)(buf + 4) = arg1;
1688c2ecf20Sopenharmony_ci	*(u32 *)(buf + 16) = arg2;
1698c2ecf20Sopenharmony_ci	args[0].type = ACPI_TYPE_INTEGER;
1708c2ecf20Sopenharmony_ci	args[0].integer.value = 0; /* ignored */
1718c2ecf20Sopenharmony_ci	args[1].type = ACPI_TYPE_INTEGER;
1728c2ecf20Sopenharmony_ci	args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */
1738c2ecf20Sopenharmony_ci	args[2].type = ACPI_TYPE_BUFFER;
1748c2ecf20Sopenharmony_ci	args[2].buffer.length = 32;
1758c2ecf20Sopenharmony_ci	args[2].buffer.pointer = buf;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle);
1788c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1798c2ecf20Sopenharmony_ci		pr_err("Cannot get handle");
1808c2ecf20Sopenharmony_ci		return NULL;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	arg.count = 3;
1848c2ecf20Sopenharmony_ci	arg.pointer = args;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
1878c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
1888c2ecf20Sopenharmony_ci		acpi_handle_err(handle, "WMAB: call failed.\n");
1898c2ecf20Sopenharmony_ci		return NULL;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return (union acpi_object *)buffer.pointer;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void wmi_notify(u32 value, void *context)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
1988c2ecf20Sopenharmony_ci	union acpi_object *obj;
1998c2ecf20Sopenharmony_ci	acpi_status status;
2008c2ecf20Sopenharmony_ci	long data = (long)context;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	pr_debug("event guid %li\n", data);
2038c2ecf20Sopenharmony_ci	status = wmi_get_event_data(value, &response);
2048c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
2058c2ecf20Sopenharmony_ci		pr_err("Bad event status 0x%x\n", status);
2068c2ecf20Sopenharmony_ci		return;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	obj = (union acpi_object *)response.pointer;
2108c2ecf20Sopenharmony_ci	if (!obj)
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (obj->type == ACPI_TYPE_INTEGER) {
2148c2ecf20Sopenharmony_ci		int eventcode = obj->integer.value;
2158c2ecf20Sopenharmony_ci		struct key_entry *key;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		key =
2188c2ecf20Sopenharmony_ci		    sparse_keymap_entry_from_scancode(wmi_input_dev, eventcode);
2198c2ecf20Sopenharmony_ci		if (key && key->type == KE_KEY)
2208c2ecf20Sopenharmony_ci			sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	pr_debug("Type: %i    Eventcode: 0x%llx\n", obj->type,
2248c2ecf20Sopenharmony_ci		 obj->integer.value);
2258c2ecf20Sopenharmony_ci	kfree(response.pointer);
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void wmi_input_setup(void)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	acpi_status status;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	wmi_input_dev = input_allocate_device();
2338c2ecf20Sopenharmony_ci	if (wmi_input_dev) {
2348c2ecf20Sopenharmony_ci		wmi_input_dev->name = "LG WMI hotkeys";
2358c2ecf20Sopenharmony_ci		wmi_input_dev->phys = "wmi/input0";
2368c2ecf20Sopenharmony_ci		wmi_input_dev->id.bustype = BUS_HOST;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
2398c2ecf20Sopenharmony_ci		    input_register_device(wmi_input_dev)) {
2408c2ecf20Sopenharmony_ci			pr_info("Cannot initialize input device");
2418c2ecf20Sopenharmony_ci			input_free_device(wmi_input_dev);
2428c2ecf20Sopenharmony_ci			return;
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		inited |= INIT_SPARSE_KEYMAP;
2468c2ecf20Sopenharmony_ci		status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
2478c2ecf20Sopenharmony_ci						    (void *)0);
2488c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status))
2498c2ecf20Sopenharmony_ci			inited |= INIT_INPUT_WMI_0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
2528c2ecf20Sopenharmony_ci						    (void *)2);
2538c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status))
2548c2ecf20Sopenharmony_ci			inited |= INIT_INPUT_WMI_2;
2558c2ecf20Sopenharmony_ci	} else {
2568c2ecf20Sopenharmony_ci		pr_info("Cannot allocate input device");
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void acpi_notify(struct acpi_device *device, u32 event)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct key_entry *key;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	acpi_handle_debug(device->handle, "notify: %d\n", event);
2658c2ecf20Sopenharmony_ci	if (inited & INIT_SPARSE_KEYMAP) {
2668c2ecf20Sopenharmony_ci		key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
2678c2ecf20Sopenharmony_ci		if (key && key->type == KE_KEY)
2688c2ecf20Sopenharmony_ci			sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic ssize_t fan_mode_store(struct device *dev,
2738c2ecf20Sopenharmony_ci			      struct device_attribute *attr,
2748c2ecf20Sopenharmony_ci			      const char *buffer, size_t count)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	bool value;
2778c2ecf20Sopenharmony_ci	union acpi_object *r;
2788c2ecf20Sopenharmony_ci	u32 m;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	ret = kstrtobool(buffer, &value);
2828c2ecf20Sopenharmony_ci	if (ret)
2838c2ecf20Sopenharmony_ci		return ret;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
2868c2ecf20Sopenharmony_ci	if (!r)
2878c2ecf20Sopenharmony_ci		return -EIO;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_INTEGER) {
2908c2ecf20Sopenharmony_ci		kfree(r);
2918c2ecf20Sopenharmony_ci		return -EIO;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	m = r->integer.value;
2958c2ecf20Sopenharmony_ci	kfree(r);
2968c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
2978c2ecf20Sopenharmony_ci	kfree(r);
2988c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
2998c2ecf20Sopenharmony_ci	kfree(r);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	return count;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic ssize_t fan_mode_show(struct device *dev,
3058c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buffer)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	unsigned int status;
3088c2ecf20Sopenharmony_ci	union acpi_object *r;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
3118c2ecf20Sopenharmony_ci	if (!r)
3128c2ecf20Sopenharmony_ci		return -EIO;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_INTEGER) {
3158c2ecf20Sopenharmony_ci		kfree(r);
3168c2ecf20Sopenharmony_ci		return -EIO;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	status = r->integer.value & 0x01;
3208c2ecf20Sopenharmony_ci	kfree(r);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", status);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic ssize_t usb_charge_store(struct device *dev,
3268c2ecf20Sopenharmony_ci				struct device_attribute *attr,
3278c2ecf20Sopenharmony_ci				const char *buffer, size_t count)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	bool value;
3308c2ecf20Sopenharmony_ci	union acpi_object *r;
3318c2ecf20Sopenharmony_ci	int ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ret = kstrtobool(buffer, &value);
3348c2ecf20Sopenharmony_ci	if (ret)
3358c2ecf20Sopenharmony_ci		return ret;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value);
3388c2ecf20Sopenharmony_ci	if (!r)
3398c2ecf20Sopenharmony_ci		return -EIO;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	kfree(r);
3428c2ecf20Sopenharmony_ci	return count;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic ssize_t usb_charge_show(struct device *dev,
3468c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buffer)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	unsigned int status;
3498c2ecf20Sopenharmony_ci	union acpi_object *r;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0);
3528c2ecf20Sopenharmony_ci	if (!r)
3538c2ecf20Sopenharmony_ci		return -EIO;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_BUFFER) {
3568c2ecf20Sopenharmony_ci		kfree(r);
3578c2ecf20Sopenharmony_ci		return -EIO;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	status = !!r->buffer.pointer[0x10];
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	kfree(r);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", status);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic ssize_t reader_mode_store(struct device *dev,
3688c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3698c2ecf20Sopenharmony_ci				 const char *buffer, size_t count)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	bool value;
3728c2ecf20Sopenharmony_ci	union acpi_object *r;
3738c2ecf20Sopenharmony_ci	int ret;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	ret = kstrtobool(buffer, &value);
3768c2ecf20Sopenharmony_ci	if (ret)
3778c2ecf20Sopenharmony_ci		return ret;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	r = lg_wmab(WM_READER_MODE, WM_SET, value);
3808c2ecf20Sopenharmony_ci	if (!r)
3818c2ecf20Sopenharmony_ci		return -EIO;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	kfree(r);
3848c2ecf20Sopenharmony_ci	return count;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic ssize_t reader_mode_show(struct device *dev,
3888c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buffer)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	unsigned int status;
3918c2ecf20Sopenharmony_ci	union acpi_object *r;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	r = lg_wmab(WM_READER_MODE, WM_GET, 0);
3948c2ecf20Sopenharmony_ci	if (!r)
3958c2ecf20Sopenharmony_ci		return -EIO;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_INTEGER) {
3988c2ecf20Sopenharmony_ci		kfree(r);
3998c2ecf20Sopenharmony_ci		return -EIO;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	status = !!r->integer.value;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	kfree(r);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", status);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic ssize_t fn_lock_store(struct device *dev,
4108c2ecf20Sopenharmony_ci			     struct device_attribute *attr,
4118c2ecf20Sopenharmony_ci			     const char *buffer, size_t count)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	bool value;
4148c2ecf20Sopenharmony_ci	union acpi_object *r;
4158c2ecf20Sopenharmony_ci	int ret;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ret = kstrtobool(buffer, &value);
4188c2ecf20Sopenharmony_ci	if (ret)
4198c2ecf20Sopenharmony_ci		return ret;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FN_LOCK, WM_SET, value);
4228c2ecf20Sopenharmony_ci	if (!r)
4238c2ecf20Sopenharmony_ci		return -EIO;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	kfree(r);
4268c2ecf20Sopenharmony_ci	return count;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic ssize_t fn_lock_show(struct device *dev,
4308c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buffer)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	unsigned int status;
4338c2ecf20Sopenharmony_ci	union acpi_object *r;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	r = lg_wmab(WM_FN_LOCK, WM_GET, 0);
4368c2ecf20Sopenharmony_ci	if (!r)
4378c2ecf20Sopenharmony_ci		return -EIO;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_BUFFER) {
4408c2ecf20Sopenharmony_ci		kfree(r);
4418c2ecf20Sopenharmony_ci		return -EIO;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	status = !!r->buffer.pointer[0];
4458c2ecf20Sopenharmony_ci	kfree(r);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", status);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic ssize_t battery_care_limit_store(struct device *dev,
4518c2ecf20Sopenharmony_ci					struct device_attribute *attr,
4528c2ecf20Sopenharmony_ci					const char *buffer, size_t count)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	unsigned long value;
4558c2ecf20Sopenharmony_ci	int ret;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	ret = kstrtoul(buffer, 10, &value);
4588c2ecf20Sopenharmony_ci	if (ret)
4598c2ecf20Sopenharmony_ci		return ret;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (value == 100 || value == 80) {
4628c2ecf20Sopenharmony_ci		union acpi_object *r;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		r = lg_wmab(WM_BATT_LIMIT, WM_SET, value);
4658c2ecf20Sopenharmony_ci		if (!r)
4668c2ecf20Sopenharmony_ci			return -EIO;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		kfree(r);
4698c2ecf20Sopenharmony_ci		return count;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return -EINVAL;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic ssize_t battery_care_limit_show(struct device *dev,
4768c2ecf20Sopenharmony_ci				       struct device_attribute *attr,
4778c2ecf20Sopenharmony_ci				       char *buffer)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	unsigned int status;
4808c2ecf20Sopenharmony_ci	union acpi_object *r;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0);
4838c2ecf20Sopenharmony_ci	if (!r)
4848c2ecf20Sopenharmony_ci		return -EIO;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_INTEGER) {
4878c2ecf20Sopenharmony_ci		kfree(r);
4888c2ecf20Sopenharmony_ci		return -EIO;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	status = r->integer.value;
4928c2ecf20Sopenharmony_ci	kfree(r);
4938c2ecf20Sopenharmony_ci	if (status != 80 && status != 100)
4948c2ecf20Sopenharmony_ci		status = 0;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return snprintf(buffer, PAGE_SIZE, "%d\n", status);
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fan_mode);
5008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(usb_charge);
5018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(reader_mode);
5028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fn_lock);
5038c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(battery_care_limit);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic struct attribute *dev_attributes[] = {
5068c2ecf20Sopenharmony_ci	&dev_attr_fan_mode.attr,
5078c2ecf20Sopenharmony_ci	&dev_attr_usb_charge.attr,
5088c2ecf20Sopenharmony_ci	&dev_attr_reader_mode.attr,
5098c2ecf20Sopenharmony_ci	&dev_attr_fn_lock.attr,
5108c2ecf20Sopenharmony_ci	&dev_attr_battery_care_limit.attr,
5118c2ecf20Sopenharmony_ci	NULL
5128c2ecf20Sopenharmony_ci};
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic const struct attribute_group dev_attribute_group = {
5158c2ecf20Sopenharmony_ci	.attrs = dev_attributes,
5168c2ecf20Sopenharmony_ci};
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic void tpad_led_set(struct led_classdev *cdev,
5198c2ecf20Sopenharmony_ci			 enum led_brightness brightness)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	union acpi_object *r;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF);
5248c2ecf20Sopenharmony_ci	kfree(r);
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic enum led_brightness tpad_led_get(struct led_classdev *cdev)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic LED_DEVICE(tpad_led, 1);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic void kbd_backlight_set(struct led_classdev *cdev,
5358c2ecf20Sopenharmony_ci			      enum led_brightness brightness)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	u32 val;
5388c2ecf20Sopenharmony_ci	union acpi_object *r;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	val = 0x22;
5418c2ecf20Sopenharmony_ci	if (brightness <= LED_OFF)
5428c2ecf20Sopenharmony_ci		val = 0;
5438c2ecf20Sopenharmony_ci	if (brightness >= LED_FULL)
5448c2ecf20Sopenharmony_ci		val = 0x24;
5458c2ecf20Sopenharmony_ci	r = lg_wmab(WM_KEY_LIGHT, WM_SET, val);
5468c2ecf20Sopenharmony_ci	kfree(r);
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	union acpi_object *r;
5528c2ecf20Sopenharmony_ci	int val;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (!r)
5578c2ecf20Sopenharmony_ci		return LED_OFF;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
5608c2ecf20Sopenharmony_ci		kfree(r);
5618c2ecf20Sopenharmony_ci		return LED_OFF;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	switch (r->buffer.pointer[0] & 0x27) {
5658c2ecf20Sopenharmony_ci	case 0x24:
5668c2ecf20Sopenharmony_ci		val = LED_FULL;
5678c2ecf20Sopenharmony_ci		break;
5688c2ecf20Sopenharmony_ci	case 0x22:
5698c2ecf20Sopenharmony_ci		val = LED_HALF;
5708c2ecf20Sopenharmony_ci		break;
5718c2ecf20Sopenharmony_ci	default:
5728c2ecf20Sopenharmony_ci		val = LED_OFF;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	kfree(r);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return val;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic LED_DEVICE(kbd_backlight, 255);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic void wmi_input_destroy(void)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	if (inited & INIT_INPUT_WMI_2)
5858c2ecf20Sopenharmony_ci		wmi_remove_notify_handler(WMI_EVENT_GUID2);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (inited & INIT_INPUT_WMI_0)
5888c2ecf20Sopenharmony_ci		wmi_remove_notify_handler(WMI_EVENT_GUID0);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (inited & INIT_SPARSE_KEYMAP)
5918c2ecf20Sopenharmony_ci		input_unregister_device(wmi_input_dev);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic struct platform_driver pf_driver = {
5978c2ecf20Sopenharmony_ci	.driver = {
5988c2ecf20Sopenharmony_ci		   .name = PLATFORM_NAME,
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int acpi_add(struct acpi_device *device)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	int ret;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if (pf_device)
6078c2ecf20Sopenharmony_ci		return 0;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	ret = platform_driver_register(&pf_driver);
6108c2ecf20Sopenharmony_ci	if (ret)
6118c2ecf20Sopenharmony_ci		return ret;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	pf_device = platform_device_register_simple(PLATFORM_NAME,
6148c2ecf20Sopenharmony_ci						    PLATFORM_DEVID_NONE,
6158c2ecf20Sopenharmony_ci						    NULL, 0);
6168c2ecf20Sopenharmony_ci	if (IS_ERR(pf_device)) {
6178c2ecf20Sopenharmony_ci		ret = PTR_ERR(pf_device);
6188c2ecf20Sopenharmony_ci		pf_device = NULL;
6198c2ecf20Sopenharmony_ci		pr_err("unable to register platform device\n");
6208c2ecf20Sopenharmony_ci		goto out_platform_registered;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
6248c2ecf20Sopenharmony_ci	if (ret)
6258c2ecf20Sopenharmony_ci		goto out_platform_device;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* LEDs are optional */
6288c2ecf20Sopenharmony_ci	led_classdev_register(&pf_device->dev, &kbd_backlight);
6298c2ecf20Sopenharmony_ci	led_classdev_register(&pf_device->dev, &tpad_led);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	wmi_input_setup();
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	return 0;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ciout_platform_device:
6368c2ecf20Sopenharmony_ci	platform_device_unregister(pf_device);
6378c2ecf20Sopenharmony_ciout_platform_registered:
6388c2ecf20Sopenharmony_ci	platform_driver_unregister(&pf_driver);
6398c2ecf20Sopenharmony_ci	return ret;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int acpi_remove(struct acpi_device *device)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	led_classdev_unregister(&tpad_led);
6478c2ecf20Sopenharmony_ci	led_classdev_unregister(&kbd_backlight);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	wmi_input_destroy();
6508c2ecf20Sopenharmony_ci	platform_device_unregister(pf_device);
6518c2ecf20Sopenharmony_ci	pf_device = NULL;
6528c2ecf20Sopenharmony_ci	platform_driver_unregister(&pf_driver);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	return 0;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic const struct acpi_device_id device_ids[] = {
6588c2ecf20Sopenharmony_ci	{"LGEX0815", 0},
6598c2ecf20Sopenharmony_ci	{"", 0}
6608c2ecf20Sopenharmony_ci};
6618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, device_ids);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_driver = {
6648c2ecf20Sopenharmony_ci	.name = "LG Gram Laptop Support",
6658c2ecf20Sopenharmony_ci	.class = "lg-laptop",
6668c2ecf20Sopenharmony_ci	.ids = device_ids,
6678c2ecf20Sopenharmony_ci	.ops = {
6688c2ecf20Sopenharmony_ci		.add = acpi_add,
6698c2ecf20Sopenharmony_ci		.remove = acpi_remove,
6708c2ecf20Sopenharmony_ci		.notify = acpi_notify,
6718c2ecf20Sopenharmony_ci		},
6728c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
6738c2ecf20Sopenharmony_ci};
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic int __init acpi_init(void)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	int result;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&acpi_driver);
6808c2ecf20Sopenharmony_ci	if (result < 0) {
6818c2ecf20Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering driver\n"));
6828c2ecf20Sopenharmony_ci		return -ENODEV;
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return 0;
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic void __exit acpi_exit(void)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&acpi_driver);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cimodule_init(acpi_init);
6948c2ecf20Sopenharmony_cimodule_exit(acpi_exit);
695