brintos

brintos / linux-shallow public Read only

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