cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lg-laptop.c (17704B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * lg-laptop.c - LG Gram ACPI features and hotkeys Driver
      4 *
      5 * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org>
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/acpi.h>
     11#include <linux/dmi.h>
     12#include <linux/input.h>
     13#include <linux/input/sparse-keymap.h>
     14#include <linux/kernel.h>
     15#include <linux/leds.h>
     16#include <linux/module.h>
     17#include <linux/platform_device.h>
     18#include <linux/types.h>
     19
     20#include <acpi/battery.h>
     21
     22#define LED_DEVICE(_name, max, flag) struct led_classdev _name = { \
     23	.name           = __stringify(_name),   \
     24	.max_brightness = max,                  \
     25	.brightness_set = _name##_set,          \
     26	.brightness_get = _name##_get,          \
     27	.flags = flag,                          \
     28}
     29
     30MODULE_AUTHOR("Matan Ziv-Av");
     31MODULE_DESCRIPTION("LG WMI Hotkey Driver");
     32MODULE_LICENSE("GPL");
     33
     34#define WMI_EVENT_GUID0	"E4FB94F9-7F2B-4173-AD1A-CD1D95086248"
     35#define WMI_EVENT_GUID1	"023B133E-49D1-4E10-B313-698220140DC2"
     36#define WMI_EVENT_GUID2	"37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6"
     37#define WMI_EVENT_GUID3	"911BAD44-7DF8-4FBB-9319-BABA1C4B293B"
     38#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D"
     39#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210"
     40#define WMI_EVENT_GUID  WMI_EVENT_GUID0
     41
     42#define WMAB_METHOD     "\\XINI.WMAB"
     43#define WMBB_METHOD     "\\XINI.WMBB"
     44#define SB_GGOV_METHOD  "\\_SB.GGOV"
     45#define GOV_TLED        0x2020008
     46#define WM_GET          1
     47#define WM_SET          2
     48#define WM_KEY_LIGHT    0x400
     49#define WM_TLED         0x404
     50#define WM_FN_LOCK      0x407
     51#define WM_BATT_LIMIT   0x61
     52#define WM_READER_MODE  0xBF
     53#define WM_FAN_MODE	0x33
     54#define WMBB_USB_CHARGE 0x10B
     55#define WMBB_BATT_LIMIT 0x10C
     56
     57#define PLATFORM_NAME   "lg-laptop"
     58
     59MODULE_ALIAS("wmi:" WMI_EVENT_GUID0);
     60MODULE_ALIAS("wmi:" WMI_EVENT_GUID1);
     61MODULE_ALIAS("wmi:" WMI_EVENT_GUID2);
     62MODULE_ALIAS("wmi:" WMI_EVENT_GUID3);
     63MODULE_ALIAS("wmi:" WMI_METHOD_WMAB);
     64MODULE_ALIAS("wmi:" WMI_METHOD_WMBB);
     65
     66static struct platform_device *pf_device;
     67static struct input_dev *wmi_input_dev;
     68
     69static u32 inited;
     70#define INIT_INPUT_WMI_0        0x01
     71#define INIT_INPUT_WMI_2        0x02
     72#define INIT_INPUT_ACPI         0x04
     73#define INIT_SPARSE_KEYMAP      0x80
     74
     75static int battery_limit_use_wmbb;
     76static struct led_classdev kbd_backlight;
     77static enum led_brightness get_kbd_backlight_level(void);
     78
     79static const struct key_entry wmi_keymap[] = {
     80	{KE_KEY, 0x70, {KEY_F15} },	 /* LG control panel (F1) */
     81	{KE_KEY, 0x74, {KEY_F21} },	 /* Touchpad toggle (F5) */
     82	{KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */
     83	{KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing
     84					  * this key both sends an event and
     85					  * changes backlight level.
     86					  */
     87	{KE_KEY, 0x80, {KEY_RFKILL} },
     88	{KE_END, 0}
     89};
     90
     91static int ggov(u32 arg0)
     92{
     93	union acpi_object args[1];
     94	union acpi_object *r;
     95	acpi_status status;
     96	acpi_handle handle;
     97	struct acpi_object_list arg;
     98	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
     99	int res;
    100
    101	args[0].type = ACPI_TYPE_INTEGER;
    102	args[0].integer.value = arg0;
    103
    104	status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle);
    105	if (ACPI_FAILURE(status)) {
    106		pr_err("Cannot get handle");
    107		return -ENODEV;
    108	}
    109
    110	arg.count = 1;
    111	arg.pointer = args;
    112
    113	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
    114	if (ACPI_FAILURE(status)) {
    115		acpi_handle_err(handle, "GGOV: call failed.\n");
    116		return -EINVAL;
    117	}
    118
    119	r = buffer.pointer;
    120	if (r->type != ACPI_TYPE_INTEGER) {
    121		kfree(r);
    122		return -EINVAL;
    123	}
    124
    125	res = r->integer.value;
    126	kfree(r);
    127
    128	return res;
    129}
    130
    131static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2)
    132{
    133	union acpi_object args[3];
    134	acpi_status status;
    135	acpi_handle handle;
    136	struct acpi_object_list arg;
    137	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
    138
    139	args[0].type = ACPI_TYPE_INTEGER;
    140	args[0].integer.value = method;
    141	args[1].type = ACPI_TYPE_INTEGER;
    142	args[1].integer.value = arg1;
    143	args[2].type = ACPI_TYPE_INTEGER;
    144	args[2].integer.value = arg2;
    145
    146	status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle);
    147	if (ACPI_FAILURE(status)) {
    148		pr_err("Cannot get handle");
    149		return NULL;
    150	}
    151
    152	arg.count = 3;
    153	arg.pointer = args;
    154
    155	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
    156	if (ACPI_FAILURE(status)) {
    157		acpi_handle_err(handle, "WMAB: call failed.\n");
    158		return NULL;
    159	}
    160
    161	return buffer.pointer;
    162}
    163
    164static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2)
    165{
    166	union acpi_object args[3];
    167	acpi_status status;
    168	acpi_handle handle;
    169	struct acpi_object_list arg;
    170	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
    171	u8 buf[32];
    172
    173	*(u32 *)buf = method_id;
    174	*(u32 *)(buf + 4) = arg1;
    175	*(u32 *)(buf + 16) = arg2;
    176	args[0].type = ACPI_TYPE_INTEGER;
    177	args[0].integer.value = 0; /* ignored */
    178	args[1].type = ACPI_TYPE_INTEGER;
    179	args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */
    180	args[2].type = ACPI_TYPE_BUFFER;
    181	args[2].buffer.length = 32;
    182	args[2].buffer.pointer = buf;
    183
    184	status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle);
    185	if (ACPI_FAILURE(status)) {
    186		pr_err("Cannot get handle");
    187		return NULL;
    188	}
    189
    190	arg.count = 3;
    191	arg.pointer = args;
    192
    193	status = acpi_evaluate_object(handle, NULL, &arg, &buffer);
    194	if (ACPI_FAILURE(status)) {
    195		acpi_handle_err(handle, "WMAB: call failed.\n");
    196		return NULL;
    197	}
    198
    199	return (union acpi_object *)buffer.pointer;
    200}
    201
    202static void wmi_notify(u32 value, void *context)
    203{
    204	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
    205	union acpi_object *obj;
    206	acpi_status status;
    207	long data = (long)context;
    208
    209	pr_debug("event guid %li\n", data);
    210	status = wmi_get_event_data(value, &response);
    211	if (ACPI_FAILURE(status)) {
    212		pr_err("Bad event status 0x%x\n", status);
    213		return;
    214	}
    215
    216	obj = (union acpi_object *)response.pointer;
    217	if (!obj)
    218		return;
    219
    220	if (obj->type == ACPI_TYPE_INTEGER) {
    221		int eventcode = obj->integer.value;
    222		struct key_entry *key;
    223
    224		if (eventcode == 0x10000000) {
    225			led_classdev_notify_brightness_hw_changed(
    226				&kbd_backlight, get_kbd_backlight_level());
    227		} else {
    228			key = sparse_keymap_entry_from_scancode(
    229				wmi_input_dev, eventcode);
    230			if (key && key->type == KE_KEY)
    231				sparse_keymap_report_entry(wmi_input_dev,
    232							   key, 1, true);
    233		}
    234	}
    235
    236	pr_debug("Type: %i    Eventcode: 0x%llx\n", obj->type,
    237		 obj->integer.value);
    238	kfree(response.pointer);
    239}
    240
    241static void wmi_input_setup(void)
    242{
    243	acpi_status status;
    244
    245	wmi_input_dev = input_allocate_device();
    246	if (wmi_input_dev) {
    247		wmi_input_dev->name = "LG WMI hotkeys";
    248		wmi_input_dev->phys = "wmi/input0";
    249		wmi_input_dev->id.bustype = BUS_HOST;
    250
    251		if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) ||
    252		    input_register_device(wmi_input_dev)) {
    253			pr_info("Cannot initialize input device");
    254			input_free_device(wmi_input_dev);
    255			return;
    256		}
    257
    258		inited |= INIT_SPARSE_KEYMAP;
    259		status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify,
    260						    (void *)0);
    261		if (ACPI_SUCCESS(status))
    262			inited |= INIT_INPUT_WMI_0;
    263
    264		status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify,
    265						    (void *)2);
    266		if (ACPI_SUCCESS(status))
    267			inited |= INIT_INPUT_WMI_2;
    268	} else {
    269		pr_info("Cannot allocate input device");
    270	}
    271}
    272
    273static void acpi_notify(struct acpi_device *device, u32 event)
    274{
    275	struct key_entry *key;
    276
    277	acpi_handle_debug(device->handle, "notify: %d\n", event);
    278	if (inited & INIT_SPARSE_KEYMAP) {
    279		key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80);
    280		if (key && key->type == KE_KEY)
    281			sparse_keymap_report_entry(wmi_input_dev, key, 1, true);
    282	}
    283}
    284
    285static ssize_t fan_mode_store(struct device *dev,
    286			      struct device_attribute *attr,
    287			      const char *buffer, size_t count)
    288{
    289	bool value;
    290	union acpi_object *r;
    291	u32 m;
    292	int ret;
    293
    294	ret = kstrtobool(buffer, &value);
    295	if (ret)
    296		return ret;
    297
    298	r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
    299	if (!r)
    300		return -EIO;
    301
    302	if (r->type != ACPI_TYPE_INTEGER) {
    303		kfree(r);
    304		return -EIO;
    305	}
    306
    307	m = r->integer.value;
    308	kfree(r);
    309	r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4));
    310	kfree(r);
    311	r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value);
    312	kfree(r);
    313
    314	return count;
    315}
    316
    317static ssize_t fan_mode_show(struct device *dev,
    318			     struct device_attribute *attr, char *buffer)
    319{
    320	unsigned int status;
    321	union acpi_object *r;
    322
    323	r = lg_wmab(WM_FAN_MODE, WM_GET, 0);
    324	if (!r)
    325		return -EIO;
    326
    327	if (r->type != ACPI_TYPE_INTEGER) {
    328		kfree(r);
    329		return -EIO;
    330	}
    331
    332	status = r->integer.value & 0x01;
    333	kfree(r);
    334
    335	return sysfs_emit(buffer, "%d\n", status);
    336}
    337
    338static ssize_t usb_charge_store(struct device *dev,
    339				struct device_attribute *attr,
    340				const char *buffer, size_t count)
    341{
    342	bool value;
    343	union acpi_object *r;
    344	int ret;
    345
    346	ret = kstrtobool(buffer, &value);
    347	if (ret)
    348		return ret;
    349
    350	r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value);
    351	if (!r)
    352		return -EIO;
    353
    354	kfree(r);
    355	return count;
    356}
    357
    358static ssize_t usb_charge_show(struct device *dev,
    359			       struct device_attribute *attr, char *buffer)
    360{
    361	unsigned int status;
    362	union acpi_object *r;
    363
    364	r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0);
    365	if (!r)
    366		return -EIO;
    367
    368	if (r->type != ACPI_TYPE_BUFFER) {
    369		kfree(r);
    370		return -EIO;
    371	}
    372
    373	status = !!r->buffer.pointer[0x10];
    374
    375	kfree(r);
    376
    377	return sysfs_emit(buffer, "%d\n", status);
    378}
    379
    380static ssize_t reader_mode_store(struct device *dev,
    381				 struct device_attribute *attr,
    382				 const char *buffer, size_t count)
    383{
    384	bool value;
    385	union acpi_object *r;
    386	int ret;
    387
    388	ret = kstrtobool(buffer, &value);
    389	if (ret)
    390		return ret;
    391
    392	r = lg_wmab(WM_READER_MODE, WM_SET, value);
    393	if (!r)
    394		return -EIO;
    395
    396	kfree(r);
    397	return count;
    398}
    399
    400static ssize_t reader_mode_show(struct device *dev,
    401				struct device_attribute *attr, char *buffer)
    402{
    403	unsigned int status;
    404	union acpi_object *r;
    405
    406	r = lg_wmab(WM_READER_MODE, WM_GET, 0);
    407	if (!r)
    408		return -EIO;
    409
    410	if (r->type != ACPI_TYPE_INTEGER) {
    411		kfree(r);
    412		return -EIO;
    413	}
    414
    415	status = !!r->integer.value;
    416
    417	kfree(r);
    418
    419	return sysfs_emit(buffer, "%d\n", status);
    420}
    421
    422static ssize_t fn_lock_store(struct device *dev,
    423			     struct device_attribute *attr,
    424			     const char *buffer, size_t count)
    425{
    426	bool value;
    427	union acpi_object *r;
    428	int ret;
    429
    430	ret = kstrtobool(buffer, &value);
    431	if (ret)
    432		return ret;
    433
    434	r = lg_wmab(WM_FN_LOCK, WM_SET, value);
    435	if (!r)
    436		return -EIO;
    437
    438	kfree(r);
    439	return count;
    440}
    441
    442static ssize_t fn_lock_show(struct device *dev,
    443			    struct device_attribute *attr, char *buffer)
    444{
    445	unsigned int status;
    446	union acpi_object *r;
    447
    448	r = lg_wmab(WM_FN_LOCK, WM_GET, 0);
    449	if (!r)
    450		return -EIO;
    451
    452	if (r->type != ACPI_TYPE_BUFFER) {
    453		kfree(r);
    454		return -EIO;
    455	}
    456
    457	status = !!r->buffer.pointer[0];
    458	kfree(r);
    459
    460	return sysfs_emit(buffer, "%d\n", status);
    461}
    462
    463static ssize_t charge_control_end_threshold_store(struct device *dev,
    464						  struct device_attribute *attr,
    465						  const char *buf, size_t count)
    466{
    467	unsigned long value;
    468	int ret;
    469
    470	ret = kstrtoul(buf, 10, &value);
    471	if (ret)
    472		return ret;
    473
    474	if (value == 100 || value == 80) {
    475		union acpi_object *r;
    476
    477		if (battery_limit_use_wmbb)
    478			r = lg_wmbb(WMBB_BATT_LIMIT, WM_SET, value);
    479		else
    480			r = lg_wmab(WM_BATT_LIMIT, WM_SET, value);
    481		if (!r)
    482			return -EIO;
    483
    484		kfree(r);
    485		return count;
    486	}
    487
    488	return -EINVAL;
    489}
    490
    491static ssize_t charge_control_end_threshold_show(struct device *device,
    492						 struct device_attribute *attr,
    493						 char *buf)
    494{
    495	unsigned int status;
    496	union acpi_object *r;
    497
    498	if (battery_limit_use_wmbb) {
    499		r = lg_wmbb(WMBB_BATT_LIMIT, WM_GET, 0);
    500		if (!r)
    501			return -EIO;
    502
    503		if (r->type != ACPI_TYPE_BUFFER) {
    504			kfree(r);
    505			return -EIO;
    506		}
    507
    508		status = r->buffer.pointer[0x10];
    509	} else {
    510		r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0);
    511		if (!r)
    512			return -EIO;
    513
    514		if (r->type != ACPI_TYPE_INTEGER) {
    515			kfree(r);
    516			return -EIO;
    517		}
    518
    519		status = r->integer.value;
    520	}
    521	kfree(r);
    522	if (status != 80 && status != 100)
    523		status = 0;
    524
    525	return sysfs_emit(buf, "%d\n", status);
    526}
    527
    528static ssize_t battery_care_limit_show(struct device *dev,
    529				       struct device_attribute *attr,
    530				       char *buffer)
    531{
    532	return charge_control_end_threshold_show(dev, attr, buffer);
    533}
    534
    535static ssize_t battery_care_limit_store(struct device *dev,
    536					struct device_attribute *attr,
    537					const char *buffer, size_t count)
    538{
    539	return charge_control_end_threshold_store(dev, attr, buffer, count);
    540}
    541
    542static DEVICE_ATTR_RW(fan_mode);
    543static DEVICE_ATTR_RW(usb_charge);
    544static DEVICE_ATTR_RW(reader_mode);
    545static DEVICE_ATTR_RW(fn_lock);
    546static DEVICE_ATTR_RW(charge_control_end_threshold);
    547static DEVICE_ATTR_RW(battery_care_limit);
    548
    549static int lg_battery_add(struct power_supply *battery)
    550{
    551	if (device_create_file(&battery->dev,
    552			       &dev_attr_charge_control_end_threshold))
    553		return -ENODEV;
    554
    555	return 0;
    556}
    557
    558static int lg_battery_remove(struct power_supply *battery)
    559{
    560	device_remove_file(&battery->dev,
    561			   &dev_attr_charge_control_end_threshold);
    562	return 0;
    563}
    564
    565static struct acpi_battery_hook battery_hook = {
    566	.add_battery = lg_battery_add,
    567	.remove_battery = lg_battery_remove,
    568	.name = "LG Battery Extension",
    569};
    570
    571static struct attribute *dev_attributes[] = {
    572	&dev_attr_fan_mode.attr,
    573	&dev_attr_usb_charge.attr,
    574	&dev_attr_reader_mode.attr,
    575	&dev_attr_fn_lock.attr,
    576	&dev_attr_battery_care_limit.attr,
    577	NULL
    578};
    579
    580static const struct attribute_group dev_attribute_group = {
    581	.attrs = dev_attributes,
    582};
    583
    584static void tpad_led_set(struct led_classdev *cdev,
    585			 enum led_brightness brightness)
    586{
    587	union acpi_object *r;
    588
    589	r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF);
    590	kfree(r);
    591}
    592
    593static enum led_brightness tpad_led_get(struct led_classdev *cdev)
    594{
    595	return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF;
    596}
    597
    598static LED_DEVICE(tpad_led, 1, 0);
    599
    600static void kbd_backlight_set(struct led_classdev *cdev,
    601			      enum led_brightness brightness)
    602{
    603	u32 val;
    604	union acpi_object *r;
    605
    606	val = 0x22;
    607	if (brightness <= LED_OFF)
    608		val = 0;
    609	if (brightness >= LED_FULL)
    610		val = 0x24;
    611	r = lg_wmab(WM_KEY_LIGHT, WM_SET, val);
    612	kfree(r);
    613}
    614
    615static enum led_brightness get_kbd_backlight_level(void)
    616{
    617	union acpi_object *r;
    618	int val;
    619
    620	r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0);
    621
    622	if (!r)
    623		return LED_OFF;
    624
    625	if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) {
    626		kfree(r);
    627		return LED_OFF;
    628	}
    629
    630	switch (r->buffer.pointer[0] & 0x27) {
    631	case 0x24:
    632		val = LED_FULL;
    633		break;
    634	case 0x22:
    635		val = LED_HALF;
    636		break;
    637	default:
    638		val = LED_OFF;
    639	}
    640
    641	kfree(r);
    642
    643	return val;
    644}
    645
    646static enum led_brightness kbd_backlight_get(struct led_classdev *cdev)
    647{
    648	return get_kbd_backlight_level();
    649}
    650
    651static LED_DEVICE(kbd_backlight, 255, LED_BRIGHT_HW_CHANGED);
    652
    653static void wmi_input_destroy(void)
    654{
    655	if (inited & INIT_INPUT_WMI_2)
    656		wmi_remove_notify_handler(WMI_EVENT_GUID2);
    657
    658	if (inited & INIT_INPUT_WMI_0)
    659		wmi_remove_notify_handler(WMI_EVENT_GUID0);
    660
    661	if (inited & INIT_SPARSE_KEYMAP)
    662		input_unregister_device(wmi_input_dev);
    663
    664	inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP);
    665}
    666
    667static struct platform_driver pf_driver = {
    668	.driver = {
    669		   .name = PLATFORM_NAME,
    670	}
    671};
    672
    673static int acpi_add(struct acpi_device *device)
    674{
    675	int ret;
    676	const char *product;
    677	int year = 2017;
    678
    679	if (pf_device)
    680		return 0;
    681
    682	ret = platform_driver_register(&pf_driver);
    683	if (ret)
    684		return ret;
    685
    686	pf_device = platform_device_register_simple(PLATFORM_NAME,
    687						    PLATFORM_DEVID_NONE,
    688						    NULL, 0);
    689	if (IS_ERR(pf_device)) {
    690		ret = PTR_ERR(pf_device);
    691		pf_device = NULL;
    692		pr_err("unable to register platform device\n");
    693		goto out_platform_registered;
    694	}
    695	product = dmi_get_system_info(DMI_PRODUCT_NAME);
    696	if (product && strlen(product) > 4)
    697		switch (product[4]) {
    698		case '5':
    699			if (strlen(product) > 5)
    700				switch (product[5]) {
    701				case 'N':
    702					year = 2021;
    703					break;
    704				case '0':
    705					year = 2016;
    706					break;
    707				default:
    708					year = 2022;
    709				}
    710			break;
    711		case '6':
    712			year = 2016;
    713			break;
    714		case '7':
    715			year = 2017;
    716			break;
    717		case '8':
    718			year = 2018;
    719			break;
    720		case '9':
    721			year = 2019;
    722			break;
    723		case '0':
    724			if (strlen(product) > 5)
    725				switch (product[5]) {
    726				case 'N':
    727					year = 2020;
    728					break;
    729				case 'P':
    730					year = 2021;
    731					break;
    732				default:
    733					year = 2022;
    734				}
    735			break;
    736		default:
    737			year = 2019;
    738		}
    739	pr_info("product: %s  year: %d\n", product, year);
    740
    741	if (year >= 2019)
    742		battery_limit_use_wmbb = 1;
    743
    744	ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group);
    745	if (ret)
    746		goto out_platform_device;
    747
    748	/* LEDs are optional */
    749	led_classdev_register(&pf_device->dev, &kbd_backlight);
    750	led_classdev_register(&pf_device->dev, &tpad_led);
    751
    752	wmi_input_setup();
    753	battery_hook_register(&battery_hook);
    754
    755	return 0;
    756
    757out_platform_device:
    758	platform_device_unregister(pf_device);
    759out_platform_registered:
    760	platform_driver_unregister(&pf_driver);
    761	return ret;
    762}
    763
    764static int acpi_remove(struct acpi_device *device)
    765{
    766	sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group);
    767
    768	led_classdev_unregister(&tpad_led);
    769	led_classdev_unregister(&kbd_backlight);
    770
    771	battery_hook_unregister(&battery_hook);
    772	wmi_input_destroy();
    773	platform_device_unregister(pf_device);
    774	pf_device = NULL;
    775	platform_driver_unregister(&pf_driver);
    776
    777	return 0;
    778}
    779
    780static const struct acpi_device_id device_ids[] = {
    781	{"LGEX0815", 0},
    782	{"", 0}
    783};
    784MODULE_DEVICE_TABLE(acpi, device_ids);
    785
    786static struct acpi_driver acpi_driver = {
    787	.name = "LG Gram Laptop Support",
    788	.class = "lg-laptop",
    789	.ids = device_ids,
    790	.ops = {
    791		.add = acpi_add,
    792		.remove = acpi_remove,
    793		.notify = acpi_notify,
    794		},
    795	.owner = THIS_MODULE,
    796};
    797
    798static int __init acpi_init(void)
    799{
    800	int result;
    801
    802	result = acpi_bus_register_driver(&acpi_driver);
    803	if (result < 0) {
    804		pr_debug("Error registering driver\n");
    805		return -ENODEV;
    806	}
    807
    808	return 0;
    809}
    810
    811static void __exit acpi_exit(void)
    812{
    813	acpi_bus_unregister_driver(&acpi_driver);
    814}
    815
    816module_init(acpi_init);
    817module_exit(acpi_exit);