File Index
Files and directories
Sign in
FILE

asus-armoury.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/asus-armoury.c C
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Asus Armoury (WMI) attributes driver.
   4 *
   5 * This driver uses the fw_attributes class to expose various WMI functions
   6 * that are present in many gaming and some non-gaming ASUS laptops.
   7 *
   8 * These typically don't fit anywhere else in the sysfs such as under LED class,
   9 * hwmon or others, and are set in Windows using the ASUS Armoury Crate tool.
  10 *
  11 * Copyright(C) 2024 Luke Jones <luke@ljones.dev>
  12 */
  13
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/acpi.h>
  17#include <linux/array_size.h>
  18#include <linux/bitfield.h>
  19#include <linux/device.h>
  20#include <linux/dmi.h>
  21#include <linux/err.h>
  22#include <linux/errno.h>
  23#include <linux/fs.h>
  24#include <linux/kernel.h>
  25#include <linux/kmod.h>
  26#include <linux/kobject.h>
  27#include <linux/kstrtox.h>
  28#include <linux/module.h>
  29#include <linux/mutex.h>
  30#include <linux/pci.h>
  31#include <linux/platform_data/x86/asus-wmi.h>
  32#include <linux/printk.h>
  33#include <linux/power_supply.h>
  34#include <linux/sysfs.h>
  35
  36#include "asus-armoury.h"
  37#include "firmware_attributes_class.h"
  38
  39#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
  40
  41#define ASUS_MINI_LED_MODE_MASK   GENMASK(1, 0)
  42/* Standard modes for devices with only on/off */
  43#define ASUS_MINI_LED_OFF         0x00
  44#define ASUS_MINI_LED_ON          0x01
  45/* Like "on" but the effect is more vibrant or brighter */
  46#define ASUS_MINI_LED_STRONG_MODE 0x02
  47/* New modes for devices with 3 mini-led mode types */
  48#define ASUS_MINI_LED_2024_WEAK   0x00
  49#define ASUS_MINI_LED_2024_STRONG 0x01
  50#define ASUS_MINI_LED_2024_OFF    0x02
  51
  52/* Power tunable attribute name defines */
  53#define ATTR_PPT_PL1_SPL        "ppt_pl1_spl"
  54#define ATTR_PPT_PL2_SPPT       "ppt_pl2_sppt"
  55#define ATTR_PPT_PL3_FPPT       "ppt_pl3_fppt"
  56#define ATTR_PPT_APU_SPPT       "ppt_apu_sppt"
  57#define ATTR_PPT_PLATFORM_SPPT  "ppt_platform_sppt"
  58#define ATTR_NV_DYNAMIC_BOOST   "nv_dynamic_boost"
  59#define ATTR_NV_TEMP_TARGET     "nv_temp_target"
  60#define ATTR_NV_BASE_TGP        "nv_base_tgp"
  61#define ATTR_NV_TGP             "nv_tgp"
  62
  63#define ASUS_ROG_TUNABLE_DC 0
  64#define ASUS_ROG_TUNABLE_AC 1
  65
  66struct rog_tunables {
  67	const struct power_limits *power_limits;
  68	u32 ppt_pl1_spl;			// cpu
  69	u32 ppt_pl2_sppt;			// cpu
  70	u32 ppt_pl3_fppt;			// cpu
  71	u32 ppt_apu_sppt;			// plat
  72	u32 ppt_platform_sppt;		// plat
  73
  74	u32 nv_dynamic_boost;
  75	u32 nv_temp_target;
  76	u32 nv_tgp;
  77};
  78
  79struct asus_armoury_priv {
  80	struct device *fw_attr_dev;
  81	struct kset *fw_attr_kset;
  82
  83	/*
  84	 * Mutex to protect eGPU activation/deactivation
  85	 * sequences and dGPU connection status:
  86	 * do not allow concurrent changes or changes
  87	 * before a reboot if dGPU got disabled.
  88	 */
  89	struct mutex egpu_mutex;
  90
  91	/* Index 0 for DC, 1 for AC */
  92	struct rog_tunables *rog_tunables[2];
  93
  94	u32 mini_led_dev_id;
  95	u32 gpu_mux_dev_id;
  96};
  97
  98static struct asus_armoury_priv asus_armoury = {
  99	.egpu_mutex = __MUTEX_INITIALIZER(asus_armoury.egpu_mutex),
 100};
 101
 102struct fw_attrs_group {
 103	bool pending_reboot;
 104};
 105
 106static struct fw_attrs_group fw_attrs = {
 107	.pending_reboot = false,
 108};
 109
 110struct asus_attr_group {
 111	const struct attribute_group *attr_group;
 112	u32 wmi_devid;
 113};
 114
 115static void asus_set_reboot_and_signal_event(void)
 116{
 117	fw_attrs.pending_reboot = true;
 118	kobject_uevent(&asus_armoury.fw_attr_dev->kobj, KOBJ_CHANGE);
 119}
 120
 121static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
 122{
 123	return sysfs_emit(buf, "%d\n", fw_attrs.pending_reboot);
 124}
 125
 126static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
 127
 128static bool asus_bios_requires_reboot(struct kobj_attribute *attr)
 129{
 130	return !strcmp(attr->attr.name, "gpu_mux_mode") ||
 131	       !strcmp(attr->attr.name, "panel_hd_mode");
 132}
 133
 134/**
 135 * armoury_has_devstate() - Check presence of the WMI function state.
 136 *
 137 * @dev_id: The WMI method ID to check for presence.
 138 *
 139 * Returns: true iif method is supported.
 140 */
 141static bool armoury_has_devstate(u32 dev_id)
 142{
 143	u32 retval;
 144	int status;
 145
 146	status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, 0, &retval);
 147	pr_debug("%s called (0x%08x), retval: 0x%08x\n", __func__, dev_id, retval);
 148
 149	return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT);
 150}
 151
 152/**
 153 * armoury_get_devstate() - Get the WMI function state.
 154 * @attr: NULL or the kobj_attribute associated to called WMI function.
 155 * @dev_id: The WMI method ID to call.
 156 * @retval:
 157 * * non-NULL pointer to where to store the value returned from WMI
 158 * * with the function presence bit cleared.
 159 *
 160 * Intended usage is from sysfs attribute checking associated WMI function.
 161 *
 162 * Returns:
 163 * * %-ENODEV	- method ID is unsupported.
 164 * * %0		- successful and retval is filled.
 165 * * %other	- error from WMI call.
 166 */
 167static int armoury_get_devstate(struct kobj_attribute *attr, u32 *retval, u32 dev_id)
 168{
 169	int err;
 170
 171	err = asus_wmi_get_devstate_dsts(dev_id, retval);
 172	if (err) {
 173		if (attr)
 174			pr_err("Failed to get %s: %d\n", attr->attr.name, err);
 175		else
 176			pr_err("Failed to get devstate for 0x%x: %d\n", dev_id, err);
 177
 178		return err;
 179	}
 180
 181	/*
 182	 * asus_wmi_get_devstate_dsts will populate retval with WMI return, but
 183	 * the true value is expressed when ASUS_WMI_DSTS_PRESENCE_BIT is clear.
 184	 */
 185	*retval &= ~ASUS_WMI_DSTS_PRESENCE_BIT;
 186
 187	return 0;
 188}
 189
 190/**
 191 * armoury_set_devstate() - Set the WMI function state.
 192 * @attr: The kobj_attribute associated to called WMI function.
 193 * @dev_id: The WMI method ID to call.
 194 * @value: The new value to be set.
 195 * @retval: Where to store the value returned from WMI or NULL.
 196 *
 197 * Intended usage is from sysfs attribute setting associated WMI function.
 198 * Before calling the presence of the function should be checked.
 199 *
 200 * Every WMI write MUST go through this function to enforce safety checks.
 201 *
 202 * Results !1 is usually considered a fail by ASUS, but some WMI methods
 203 * (like eGPU or CPU cores) do use > 1 to return a status code or similar:
 204 * in these cases caller is interested in the actual return value
 205 * and should perform relevant checks.
 206 *
 207 * Returns:
 208 * * %-EINVAL	- attempt to set a dangerous or unsupported value.
 209 * * %-EIO	- WMI function returned an error.
 210 * * %0		- successful and retval is filled.
 211 * * %other	- error from WMI call.
 212 */
 213static int armoury_set_devstate(struct kobj_attribute *attr,
 214				     u32 value, u32 *retval, u32 dev_id)
 215{
 216	u32 result;
 217	int err;
 218
 219	/*
 220	 * Prevent developers from bricking devices or issuing dangerous
 221	 * commands that can be difficult or impossible to recover from.
 222	 */
 223	switch (dev_id) {
 224	case ASUS_WMI_DEVID_APU_MEM:
 225		/*
 226		 * A hard reset might suffice to save the device,
 227		 * but there is no value in sending these commands.
 228		 */
 229		if (value == 0x100 || value == 0x101) {
 230			pr_err("Refusing to set APU memory to unsafe value: 0x%x\n", value);
 231			return -EINVAL;
 232		}
 233		break;
 234	default:
 235		/* No problems are known for this dev_id */
 236		break;
 237	}
 238
 239	err = asus_wmi_set_devstate(dev_id, value, retval ? retval : &result);
 240	if (err) {
 241		if (attr)
 242			pr_err("Failed to set %s: %d\n", attr->attr.name, err);
 243		else
 244			pr_err("Failed to set devstate for 0x%x: %d\n", dev_id, err);
 245
 246		return err;
 247	}
 248
 249	/*
 250	 * If retval == NULL caller is uninterested in return value:
 251	 * perform the most common result check here.
 252	 */
 253	if ((retval == NULL) && (result == 0)) {
 254		pr_err("Failed to set %s: (result): 0x%x\n", attr->attr.name, result);
 255		return -EIO;
 256	}
 257
 258	return 0;
 259}
 260
 261static int armoury_attr_enum_list(char *buf, size_t enum_values)
 262{
 263	size_t i;
 264	int len = 0;
 265
 266	for (i = 0; i < enum_values; i++) {
 267		if (i == 0)
 268			len += sysfs_emit_at(buf, len, "%zu", i);
 269		else
 270			len += sysfs_emit_at(buf, len, ";%zu", i);
 271	}
 272	len += sysfs_emit_at(buf, len, "\n");
 273
 274	return len;
 275}
 276
 277ssize_t armoury_attr_uint_store(struct kobject *kobj, struct kobj_attribute *attr,
 278				const char *buf, size_t count, u32 min, u32 max,
 279				u32 *store_value, u32 wmi_dev)
 280{
 281	u32 value;
 282	int err;
 283
 284	err = kstrtou32(buf, 10, &value);
 285	if (err)
 286		return err;
 287
 288	if (value < min || value > max)
 289		return -EINVAL;
 290
 291	err = armoury_set_devstate(attr, value, NULL, wmi_dev);
 292	if (err)
 293		return err;
 294
 295	if (store_value != NULL)
 296		*store_value = value;
 297	sysfs_notify(kobj, NULL, attr->attr.name);
 298
 299	if (asus_bios_requires_reboot(attr))
 300		asus_set_reboot_and_signal_event();
 301
 302	return count;
 303}
 304
 305ssize_t armoury_attr_uint_show(struct kobject *kobj, struct kobj_attribute *attr,
 306				char *buf, u32 wmi_dev)
 307{
 308	u32 result;
 309	int err;
 310
 311	err = armoury_get_devstate(attr, &result, wmi_dev);
 312	if (err)
 313		return err;
 314
 315	return sysfs_emit(buf, "%u\n", result);
 316}
 317
 318static ssize_t enum_type_show(struct kobject *kobj, struct kobj_attribute *attr,
 319			      char *buf)
 320{
 321	return sysfs_emit(buf, "enumeration\n");
 322}
 323
 324static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *attr,
 325			     char *buf)
 326{
 327	return sysfs_emit(buf, "integer\n");
 328}
 329
 330/* Mini-LED mode **************************************************************/
 331
 332/* Values map for mini-led modes on 2023 and earlier models. */
 333static u32 mini_led_mode1_map[] = {
 334	[0] = ASUS_MINI_LED_OFF,
 335	[1] = ASUS_MINI_LED_ON,
 336};
 337
 338/* Values map for mini-led modes on 2024 and later models. */
 339static u32 mini_led_mode2_map[] = {
 340	[0] = ASUS_MINI_LED_2024_OFF,
 341	[1] = ASUS_MINI_LED_2024_WEAK,
 342	[2] = ASUS_MINI_LED_2024_STRONG,
 343};
 344
 345static ssize_t mini_led_mode_current_value_show(struct kobject *kobj,
 346						struct kobj_attribute *attr, char *buf)
 347{
 348	u32 *mini_led_mode_map;
 349	size_t mini_led_mode_map_size;
 350	u32 i, mode;
 351	int err;
 352
 353	switch (asus_armoury.mini_led_dev_id) {
 354	case ASUS_WMI_DEVID_MINI_LED_MODE:
 355		mini_led_mode_map = mini_led_mode1_map;
 356		mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map);
 357		break;
 358
 359	case ASUS_WMI_DEVID_MINI_LED_MODE2:
 360		mini_led_mode_map = mini_led_mode2_map;
 361		mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map);
 362		break;
 363
 364	default:
 365		pr_err("Unrecognized mini-LED device: %u\n", asus_armoury.mini_led_dev_id);
 366		return -ENODEV;
 367	}
 368
 369	err = armoury_get_devstate(attr, &mode, asus_armoury.mini_led_dev_id);
 370	if (err)
 371		return err;
 372
 373	mode = FIELD_GET(ASUS_MINI_LED_MODE_MASK, 0);
 374
 375	for (i = 0; i < mini_led_mode_map_size; i++)
 376		if (mode == mini_led_mode_map[i])
 377			return sysfs_emit(buf, "%u\n", i);
 378
 379	pr_warn("Unrecognized mini-LED mode: %u", mode);
 380	return -EINVAL;
 381}
 382
 383static ssize_t mini_led_mode_current_value_store(struct kobject *kobj,
 384						 struct kobj_attribute *attr,
 385						 const char *buf, size_t count)
 386{
 387	u32 *mini_led_mode_map;
 388	size_t mini_led_mode_map_size;
 389	u32 mode;
 390	int err;
 391
 392	err = kstrtou32(buf, 10, &mode);
 393	if (err)
 394		return err;
 395
 396	switch (asus_armoury.mini_led_dev_id) {
 397	case ASUS_WMI_DEVID_MINI_LED_MODE:
 398		mini_led_mode_map = mini_led_mode1_map;
 399		mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode1_map);
 400		break;
 401
 402	case ASUS_WMI_DEVID_MINI_LED_MODE2:
 403		mini_led_mode_map = mini_led_mode2_map;
 404		mini_led_mode_map_size = ARRAY_SIZE(mini_led_mode2_map);
 405		break;
 406
 407	default:
 408		pr_err("Unrecognized mini-LED devid: %u\n", asus_armoury.mini_led_dev_id);
 409		return -EINVAL;
 410	}
 411
 412	if (mode >= mini_led_mode_map_size) {
 413		pr_warn("mini-LED mode unrecognized device: %u\n", mode);
 414		return -ENODEV;
 415	}
 416
 417	return armoury_attr_uint_store(kobj, attr, buf, count,
 418				       0, mini_led_mode_map[mode],
 419				       NULL, asus_armoury.mini_led_dev_id);
 420}
 421
 422static ssize_t mini_led_mode_possible_values_show(struct kobject *kobj,
 423						  struct kobj_attribute *attr, char *buf)
 424{
 425	switch (asus_armoury.mini_led_dev_id) {
 426	case ASUS_WMI_DEVID_MINI_LED_MODE:
 427		return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode1_map));
 428	case ASUS_WMI_DEVID_MINI_LED_MODE2:
 429		return armoury_attr_enum_list(buf, ARRAY_SIZE(mini_led_mode2_map));
 430	default:
 431		return -ENODEV;
 432	}
 433}
 434ASUS_ATTR_GROUP_ENUM(mini_led_mode, "mini_led_mode", "Set the mini-LED backlight mode");
 435
 436static ssize_t gpu_mux_mode_current_value_store(struct kobject *kobj,
 437						struct kobj_attribute *attr,
 438						const char *buf, size_t count)
 439{
 440	int result, err;
 441	bool optimus;
 442
 443	err = kstrtobool(buf, &optimus);
 444	if (err)
 445		return err;
 446
 447	if (armoury_has_devstate(ASUS_WMI_DEVID_DGPU)) {
 448		err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_DGPU);
 449		if (err)
 450			return err;
 451		if (result && !optimus) {
 452			pr_warn("Cannot switch MUX to dGPU mode when dGPU is disabled: %02X\n",
 453				result);
 454			return -ENODEV;
 455		}
 456	}
 457
 458	if (armoury_has_devstate(ASUS_WMI_DEVID_EGPU)) {
 459		err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU);
 460		if (err)
 461			return err;
 462		if (result && !optimus) {
 463			pr_warn("Cannot switch MUX to dGPU mode when eGPU is enabled\n");
 464			return -EBUSY;
 465		}
 466	}
 467
 468	err = armoury_set_devstate(attr, optimus ? 1 : 0, NULL, asus_armoury.gpu_mux_dev_id);
 469	if (err)
 470		return err;
 471
 472	sysfs_notify(kobj, NULL, attr->attr.name);
 473	asus_set_reboot_and_signal_event();
 474
 475	return count;
 476}
 477ASUS_WMI_SHOW_INT(gpu_mux_mode_current_value, asus_armoury.gpu_mux_dev_id);
 478ASUS_ATTR_GROUP_BOOL(gpu_mux_mode, "gpu_mux_mode", "Set the GPU display MUX mode");
 479
 480static ssize_t dgpu_disable_current_value_store(struct kobject *kobj,
 481						struct kobj_attribute *attr, const char *buf,
 482						size_t count)
 483{
 484	int result, err;
 485	bool disable;
 486
 487	err = kstrtobool(buf, &disable);
 488	if (err)
 489		return err;
 490
 491	if (asus_armoury.gpu_mux_dev_id) {
 492		err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id);
 493		if (err)
 494			return err;
 495		if (!result && disable) {
 496			pr_warn("Cannot disable dGPU when the MUX is in dGPU mode\n");
 497			return -EBUSY;
 498		}
 499	}
 500
 501	scoped_guard(mutex, &asus_armoury.egpu_mutex) {
 502		err = armoury_set_devstate(attr, disable ? 1 : 0, NULL, ASUS_WMI_DEVID_DGPU);
 503		if (err)
 504			return err;
 505	}
 506
 507	sysfs_notify(kobj, NULL, attr->attr.name);
 508
 509	return count;
 510}
 511ASUS_WMI_SHOW_INT(dgpu_disable_current_value, ASUS_WMI_DEVID_DGPU);
 512ASUS_ATTR_GROUP_BOOL(dgpu_disable, "dgpu_disable", "Disable the dGPU");
 513
 514/* Values map for eGPU activation requests. */
 515static u32 egpu_status_map[] = {
 516	[0] = 0x00000000U,
 517	[1] = 0x00000001U,
 518	[2] = 0x00000101U,
 519	[3] = 0x00000201U,
 520};
 521
 522/*
 523 * armoury_pci_rescan() - Performs a PCI rescan
 524 *
 525 * Bring up any GPU that has been hotplugged in the system.
 526 */
 527static void armoury_pci_rescan(void)
 528{
 529	struct pci_bus *b = NULL;
 530
 531	pci_lock_rescan_remove();
 532	while ((b = pci_find_next_bus(b)) != NULL)
 533		pci_rescan_bus(b);
 534	pci_unlock_rescan_remove();
 535}
 536
 537/*
 538 * The ACPI call to enable the eGPU might also disable the internal dGPU,
 539 * but this is not always the case and on certain models enabling the eGPU
 540 * when the dGPU is either still active or has been disabled without rebooting
 541 * will make both GPUs malfunction and the kernel will detect many
 542 * PCI AER unrecoverable errors.
 543 */
 544static ssize_t egpu_enable_current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
 545							const char *buf, size_t count)
 546{
 547	int err;
 548	u32 requested, enable, result;
 549
 550	err = kstrtou32(buf, 10, &requested);
 551	if (err)
 552		return err;
 553
 554	if (requested >= ARRAY_SIZE(egpu_status_map))
 555		return -EINVAL;
 556	enable = egpu_status_map[requested];
 557
 558	scoped_guard(mutex, &asus_armoury.egpu_mutex) {
 559		/* Ensure the eGPU is connected before attempting to activate it. */
 560		if (enable) {
 561			err = armoury_get_devstate(NULL, &result, ASUS_WMI_DEVID_EGPU_CONNECTED);
 562			if (err) {
 563				pr_warn("Failed to get eGPU connection status: %d\n", err);
 564				return err;
 565			}
 566			if (!result) {
 567				pr_warn("Cannot activate eGPU while undetected\n");
 568				return -ENOENT;
 569			}
 570		}
 571
 572		if (asus_armoury.gpu_mux_dev_id) {
 573			err = armoury_get_devstate(NULL, &result, asus_armoury.gpu_mux_dev_id);
 574			if (err)
 575				return err;
 576
 577			if (!result && enable) {
 578				pr_warn("Cannot enable eGPU when the MUX is in dGPU mode\n");
 579				return -ENODEV;
 580			}
 581		}
 582
 583		err = armoury_set_devstate(attr, enable, &result, ASUS_WMI_DEVID_EGPU);
 584		if (err) {
 585			pr_err("Failed to set %s: %d\n", attr->attr.name, err);
 586			return err;
 587		}
 588
 589		/*
 590		 * ACPI returns value 0x01 on success and 0x02 on a partial activation:
 591		 * performing a pci rescan will bring up the device in pci-e 3.0 speed,
 592		 * after a reboot the device will work at full speed.
 593		 */
 594		switch (result) {
 595		case 0x01:
 596			/*
 597			 * When a GPU is in use it does not get disconnected even if
 598			 * the ACPI call returns a success.
 599			 */
 600			if (!enable) {
 601				err = armoury_get_devstate(attr, &result, ASUS_WMI_DEVID_EGPU);
 602				if (err) {
 603					pr_warn("Failed to ensure eGPU is deactivated: %d\n", err);
 604					return err;
 605				}
 606
 607				if (result != 0)
 608					return -EBUSY;
 609			}
 610
 611			pr_debug("Success changing the eGPU status\n");
 612			break;
 613		case 0x02:
 614			pr_info("Success changing the eGPU status, a reboot is strongly advised\n");
 615			asus_set_reboot_and_signal_event();
 616			break;
 617		default:
 618			pr_err("Failed to change the eGPU status: wmi result is 0x%x\n", result);
 619			return -EIO;
 620		}
 621	}
 622
 623	/*
 624	 * Perform a PCI rescan: on every tested model this is necessary
 625	 * to make the eGPU visible on the bus without rebooting.
 626	 */
 627	armoury_pci_rescan();
 628
 629	sysfs_notify(kobj, NULL, attr->attr.name);
 630
 631	return count;
 632}
 633
 634static ssize_t egpu_enable_current_value_show(struct kobject *kobj, struct kobj_attribute *attr,
 635						char *buf)
 636{
 637	int i, err;
 638	u32 status;
 639
 640	scoped_guard(mutex, &asus_armoury.egpu_mutex) {
 641		err = armoury_get_devstate(attr, &status, ASUS_WMI_DEVID_EGPU);
 642		if (err)
 643			return err;
 644	}
 645
 646	for (i = 0; i < ARRAY_SIZE(egpu_status_map); i++) {
 647		if (egpu_status_map[i] == status)
 648			return sysfs_emit(buf, "%u\n", i);
 649	}
 650
 651	return -EIO;
 652}
 653
 654static ssize_t egpu_enable_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr,
 655						char *buf)
 656{
 657	return armoury_attr_enum_list(buf, ARRAY_SIZE(egpu_status_map));
 658}
 659ASUS_ATTR_GROUP_ENUM(egpu_enable, "egpu_enable", "Enable the eGPU (also disables dGPU)");
 660
 661/* Device memory available to APU */
 662
 663/*
 664 * Values map for APU reserved memory (index + 1 number of GB).
 665 * Some looks out of order, but are actually correct.
 666 */
 667static u32 apu_mem_map[] = {
 668	[0] = 0x000, /* called "AUTO" on the BIOS, is the minimum available */
 669	[1] = 0x102,
 670	[2] = 0x103,
 671	[3] = 0x104,
 672	[4] = 0x105,
 673	[5] = 0x107,
 674	[6] = 0x108,
 675	[7] = 0x109,
 676	[8] = 0x106,
 677};
 678
 679static ssize_t apu_mem_current_value_show(struct kobject *kobj, struct kobj_attribute *attr,
 680					  char *buf)
 681{
 682	int err;
 683	u32 mem;
 684
 685	err = armoury_get_devstate(attr, &mem, ASUS_WMI_DEVID_APU_MEM);
 686	if (err)
 687		return err;
 688
 689	/* After 0x000 is set, a read will return 0x100 */
 690	if (mem == 0x100)
 691		return sysfs_emit(buf, "0\n");
 692
 693	for (unsigned int i = 0; i < ARRAY_SIZE(apu_mem_map); i++) {
 694		if (apu_mem_map[i] == mem)
 695			return sysfs_emit(buf, "%u\n", i);
 696	}
 697
 698	pr_warn("Unrecognised value for APU mem 0x%08x\n", mem);
 699	return -EIO;
 700}
 701
 702static ssize_t apu_mem_current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
 703					   const char *buf, size_t count)
 704{
 705	int result, err;
 706	u32 requested, mem;
 707
 708	result = kstrtou32(buf, 10, &requested);
 709	if (result)
 710		return result;
 711
 712	if (requested >= ARRAY_SIZE(apu_mem_map))
 713		return -EINVAL;
 714	mem = apu_mem_map[requested];
 715
 716	err = armoury_set_devstate(attr, mem, NULL, ASUS_WMI_DEVID_APU_MEM);
 717	if (err) {
 718		pr_warn("Failed to set apu_mem 0x%x: %d\n", mem, err);
 719		return err;
 720	}
 721
 722	pr_info("APU memory changed to %uGB, reboot required\n", requested + 1);
 723	sysfs_notify(kobj, NULL, attr->attr.name);
 724
 725	asus_set_reboot_and_signal_event();
 726
 727	return count;
 728}
 729
 730static ssize_t apu_mem_possible_values_show(struct kobject *kobj, struct kobj_attribute *attr,
 731					    char *buf)
 732{
 733	return armoury_attr_enum_list(buf, ARRAY_SIZE(apu_mem_map));
 734}
 735ASUS_ATTR_GROUP_ENUM(apu_mem, "apu_mem", "Set available system RAM (in GB) for the APU to use");
 736
 737/* Define helper to access the current power mode tunable values */
 738static inline struct rog_tunables *get_current_tunables(void)
 739{
 740	if (power_supply_is_system_supplied())
 741		return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC];
 742
 743	return asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC];
 744}
 745
 746/* Simple attribute creation */
 747ASUS_ATTR_GROUP_ENUM_INT_RO(charge_mode, "charge_mode", ASUS_WMI_DEVID_CHARGE_MODE, "0;1;2\n",
 748			    "Show the current mode of charging");
 749ASUS_ATTR_GROUP_BOOL_RW(boot_sound, "boot_sound", ASUS_WMI_DEVID_BOOT_SOUND,
 750			"Set the boot POST sound");
 751ASUS_ATTR_GROUP_BOOL_RW(mcu_powersave, "mcu_powersave", ASUS_WMI_DEVID_MCU_POWERSAVE,
 752			"Set MCU powersaving mode");
 753ASUS_ATTR_GROUP_BOOL_RW(panel_od, "panel_overdrive", ASUS_WMI_DEVID_PANEL_OD,
 754			"Set the panel refresh overdrive");
 755ASUS_ATTR_GROUP_BOOL_RW(panel_hd_mode, "panel_hd_mode", ASUS_WMI_DEVID_PANEL_HD,
 756			"Set the panel HD mode to UHD<0> or FHD<1>");
 757ASUS_ATTR_GROUP_BOOL_RW(screen_auto_brightness, "screen_auto_brightness",
 758			ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS,
 759			"Set the panel brightness to Off<0> or On<1>");
 760ASUS_ATTR_GROUP_BOOL_RO(egpu_connected, "egpu_connected", ASUS_WMI_DEVID_EGPU_CONNECTED,
 761			"Show the eGPU connection status");
 762ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl1_spl, ATTR_PPT_PL1_SPL, ASUS_WMI_DEVID_PPT_PL1_SPL,
 763			    "Set the CPU slow package limit");
 764ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl2_sppt, ATTR_PPT_PL2_SPPT, ASUS_WMI_DEVID_PPT_PL2_SPPT,
 765			    "Set the CPU fast package limit");
 766ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_pl3_fppt, ATTR_PPT_PL3_FPPT, ASUS_WMI_DEVID_PPT_PL3_FPPT,
 767			    "Set the CPU fastest package limit");
 768ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_apu_sppt, ATTR_PPT_APU_SPPT, ASUS_WMI_DEVID_PPT_APU_SPPT,
 769			    "Set the APU package limit");
 770ASUS_ATTR_GROUP_ROG_TUNABLE(ppt_platform_sppt, ATTR_PPT_PLATFORM_SPPT, ASUS_WMI_DEVID_PPT_PLAT_SPPT,
 771			    "Set the platform package limit");
 772ASUS_ATTR_GROUP_ROG_TUNABLE(nv_dynamic_boost, ATTR_NV_DYNAMIC_BOOST, ASUS_WMI_DEVID_NV_DYN_BOOST,
 773			    "Set the Nvidia dynamic boost limit");
 774ASUS_ATTR_GROUP_ROG_TUNABLE(nv_temp_target, ATTR_NV_TEMP_TARGET, ASUS_WMI_DEVID_NV_THERM_TARGET,
 775			    "Set the Nvidia max thermal limit");
 776ASUS_ATTR_GROUP_ROG_TUNABLE(nv_tgp, "nv_tgp", ASUS_WMI_DEVID_DGPU_SET_TGP,
 777			    "Set the additional TGP on top of the base TGP");
 778ASUS_ATTR_GROUP_INT_VALUE_ONLY_RO(nv_base_tgp, ATTR_NV_BASE_TGP, ASUS_WMI_DEVID_DGPU_BASE_TGP,
 779				  "Read the base TGP value");
 780
 781/* If an attribute does not require any special case handling add it here */
 782static const struct asus_attr_group armoury_attr_groups[] = {
 783	{ &egpu_connected_attr_group, ASUS_WMI_DEVID_EGPU_CONNECTED },
 784	{ &egpu_enable_attr_group, ASUS_WMI_DEVID_EGPU },
 785	{ &dgpu_disable_attr_group, ASUS_WMI_DEVID_DGPU },
 786	{ &apu_mem_attr_group, ASUS_WMI_DEVID_APU_MEM },
 787
 788	{ &ppt_pl1_spl_attr_group, ASUS_WMI_DEVID_PPT_PL1_SPL },
 789	{ &ppt_pl2_sppt_attr_group, ASUS_WMI_DEVID_PPT_PL2_SPPT },
 790	{ &ppt_pl3_fppt_attr_group, ASUS_WMI_DEVID_PPT_PL3_FPPT },
 791	{ &ppt_apu_sppt_attr_group, ASUS_WMI_DEVID_PPT_APU_SPPT },
 792	{ &ppt_platform_sppt_attr_group, ASUS_WMI_DEVID_PPT_PLAT_SPPT },
 793	{ &nv_dynamic_boost_attr_group, ASUS_WMI_DEVID_NV_DYN_BOOST },
 794	{ &nv_temp_target_attr_group, ASUS_WMI_DEVID_NV_THERM_TARGET },
 795	{ &nv_base_tgp_attr_group, ASUS_WMI_DEVID_DGPU_BASE_TGP },
 796	{ &nv_tgp_attr_group, ASUS_WMI_DEVID_DGPU_SET_TGP },
 797
 798	{ &charge_mode_attr_group, ASUS_WMI_DEVID_CHARGE_MODE },
 799	{ &boot_sound_attr_group, ASUS_WMI_DEVID_BOOT_SOUND },
 800	{ &mcu_powersave_attr_group, ASUS_WMI_DEVID_MCU_POWERSAVE },
 801	{ &panel_od_attr_group, ASUS_WMI_DEVID_PANEL_OD },
 802	{ &panel_hd_mode_attr_group, ASUS_WMI_DEVID_PANEL_HD },
 803	{ &screen_auto_brightness_attr_group, ASUS_WMI_DEVID_SCREEN_AUTO_BRIGHTNESS },
 804};
 805
 806/**
 807 * is_power_tunable_attr - Determines if an attribute is a power-related tunable
 808 * @name: The name of the attribute to check
 809 *
 810 * This function checks if the given attribute name is related to power tuning.
 811 *
 812 * Return: true if the attribute is a power-related tunable, false otherwise
 813 */
 814static bool is_power_tunable_attr(const char *name)
 815{
 816	static const char * const power_tunable_attrs[] = {
 817		ATTR_PPT_PL1_SPL,	ATTR_PPT_PL2_SPPT,
 818		ATTR_PPT_PL3_FPPT,	ATTR_PPT_APU_SPPT,
 819		ATTR_PPT_PLATFORM_SPPT, ATTR_NV_DYNAMIC_BOOST,
 820		ATTR_NV_TEMP_TARGET,	ATTR_NV_BASE_TGP,
 821		ATTR_NV_TGP
 822	};
 823
 824	for (unsigned int i = 0; i < ARRAY_SIZE(power_tunable_attrs); i++) {
 825		if (!strcmp(name, power_tunable_attrs[i]))
 826			return true;
 827	}
 828
 829	return false;
 830}
 831
 832/**
 833 * has_valid_limit - Checks if a power-related attribute has a valid limit value
 834 * @name: The name of the attribute to check
 835 * @limits: Pointer to the power_limits structure containing limit values
 836 *
 837 * This function checks if a power-related attribute has a valid limit value.
 838 * It returns false if limits is NULL or if the corresponding limit value is zero.
 839 *
 840 * Return: true if the attribute has a valid limit value, false otherwise
 841 */
 842static bool has_valid_limit(const char *name, const struct power_limits *limits)
 843{
 844	u32 limit_value = 0;
 845
 846	if (!limits)
 847		return false;
 848
 849	if (!strcmp(name, ATTR_PPT_PL1_SPL))
 850		limit_value = limits->ppt_pl1_spl_max;
 851	else if (!strcmp(name, ATTR_PPT_PL2_SPPT))
 852		limit_value = limits->ppt_pl2_sppt_max;
 853	else if (!strcmp(name, ATTR_PPT_PL3_FPPT))
 854		limit_value = limits->ppt_pl3_fppt_max;
 855	else if (!strcmp(name, ATTR_PPT_APU_SPPT))
 856		limit_value = limits->ppt_apu_sppt_max;
 857	else if (!strcmp(name, ATTR_PPT_PLATFORM_SPPT))
 858		limit_value = limits->ppt_platform_sppt_max;
 859	else if (!strcmp(name, ATTR_NV_DYNAMIC_BOOST))
 860		limit_value = limits->nv_dynamic_boost_max;
 861	else if (!strcmp(name, ATTR_NV_TEMP_TARGET))
 862		limit_value = limits->nv_temp_target_max;
 863	else if (!strcmp(name, ATTR_NV_BASE_TGP) ||
 864		 !strcmp(name, ATTR_NV_TGP))
 865		limit_value = limits->nv_tgp_max;
 866
 867	return limit_value > 0;
 868}
 869
 870static int asus_fw_attr_add(void)
 871{
 872	const struct rog_tunables *const ac_rog_tunables =
 873		asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC];
 874	const struct power_limits *limits;
 875	bool should_create;
 876	const char *name;
 877	int err, i;
 878
 879	asus_armoury.fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
 880						NULL, "%s", DRIVER_NAME);
 881	if (IS_ERR(asus_armoury.fw_attr_dev)) {
 882		err = PTR_ERR(asus_armoury.fw_attr_dev);
 883		goto fail_class_get;
 884	}
 885
 886	asus_armoury.fw_attr_kset = kset_create_and_add("attributes", NULL,
 887						&asus_armoury.fw_attr_dev->kobj);
 888	if (!asus_armoury.fw_attr_kset) {
 889		err = -ENOMEM;
 890		goto err_destroy_classdev;
 891	}
 892
 893	err = sysfs_create_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
 894	if (err) {
 895		pr_err("Failed to create sysfs level attributes\n");
 896		goto err_destroy_kset;
 897	}
 898
 899	asus_armoury.mini_led_dev_id = 0;
 900	if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE))
 901		asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE;
 902	else if (armoury_has_devstate(ASUS_WMI_DEVID_MINI_LED_MODE2))
 903		asus_armoury.mini_led_dev_id = ASUS_WMI_DEVID_MINI_LED_MODE2;
 904
 905	if (asus_armoury.mini_led_dev_id) {
 906		err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
 907					 &mini_led_mode_attr_group);
 908		if (err) {
 909			pr_err("Failed to create sysfs-group for mini_led\n");
 910			goto err_remove_file;
 911		}
 912	}
 913
 914	asus_armoury.gpu_mux_dev_id = 0;
 915	if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX))
 916		asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX;
 917	else if (armoury_has_devstate(ASUS_WMI_DEVID_GPU_MUX_VIVO))
 918		asus_armoury.gpu_mux_dev_id = ASUS_WMI_DEVID_GPU_MUX_VIVO;
 919
 920	if (asus_armoury.gpu_mux_dev_id) {
 921		err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
 922					 &gpu_mux_mode_attr_group);
 923		if (err) {
 924			pr_err("Failed to create sysfs-group for gpu_mux\n");
 925			goto err_remove_mini_led_group;
 926		}
 927	}
 928
 929	for (i = 0; i < ARRAY_SIZE(armoury_attr_groups); i++) {
 930		if (!armoury_has_devstate(armoury_attr_groups[i].wmi_devid))
 931			continue;
 932
 933		/* Always create by default, unless PPT is not present */
 934		should_create = true;
 935		name = armoury_attr_groups[i].attr_group->name;
 936
 937		/* Check if this is a power-related tunable requiring limits */
 938		if (ac_rog_tunables && ac_rog_tunables->power_limits &&
 939		    is_power_tunable_attr(name)) {
 940			limits = ac_rog_tunables->power_limits;
 941			/* Check only AC: if not present then DC won't be either */
 942			should_create = has_valid_limit(name, limits);
 943			if (!should_create)
 944				pr_debug("Missing max value for tunable %s\n", name);
 945		}
 946
 947		if (should_create) {
 948			err = sysfs_create_group(&asus_armoury.fw_attr_kset->kobj,
 949						 armoury_attr_groups[i].attr_group);
 950			if (err) {
 951				pr_err("Failed to create sysfs-group for %s\n",
 952				       armoury_attr_groups[i].attr_group->name);
 953				goto err_remove_groups;
 954			}
 955		}
 956	}
 957
 958	return 0;
 959
 960err_remove_groups:
 961	while (i--) {
 962		if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid))
 963			sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj,
 964					   armoury_attr_groups[i].attr_group);
 965	}
 966	if (asus_armoury.gpu_mux_dev_id)
 967		sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
 968err_remove_mini_led_group:
 969	if (asus_armoury.mini_led_dev_id)
 970		sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
 971err_remove_file:
 972	sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
 973err_destroy_kset:
 974	kset_unregister(asus_armoury.fw_attr_kset);
 975err_destroy_classdev:
 976fail_class_get:
 977	device_destroy(&firmware_attributes_class, MKDEV(0, 0));
 978	return err;
 979}
 980
 981/* Init / exit ****************************************************************/
 982
 983/* Set up the min/max and defaults for ROG tunables */
 984static void init_rog_tunables(void)
 985{
 986	const struct power_limits *ac_limits, *dc_limits;
 987	struct rog_tunables *ac_rog_tunables = NULL, *dc_rog_tunables = NULL;
 988	const struct power_data *power_data;
 989	const struct dmi_system_id *dmi_id;
 990
 991	/* Match the system against the power_limits table */
 992	dmi_id = dmi_first_match(power_limits);
 993	if (!dmi_id) {
 994		pr_warn("No matching power limits found for this system\n");
 995		return;
 996	}
 997
 998	/* Get the power data for this system */
 999	power_data = dmi_id->driver_data;
1000	if (!power_data) {
1001		pr_info("No power data available for this system\n");
1002		return;
1003	}
1004
1005	/* Initialize AC power tunables */
1006	ac_limits = power_data->ac_data;
1007	if (ac_limits) {
1008		ac_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]),
1009				GFP_KERNEL);
1010		if (!ac_rog_tunables)
1011			goto err_nomem;
1012
1013		asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC] = ac_rog_tunables;
1014		ac_rog_tunables->power_limits = ac_limits;
1015
1016		/* Set initial AC values */
1017		ac_rog_tunables->ppt_pl1_spl =
1018			ac_limits->ppt_pl1_spl_def ?
1019				ac_limits->ppt_pl1_spl_def :
1020				ac_limits->ppt_pl1_spl_max;
1021
1022		ac_rog_tunables->ppt_pl2_sppt =
1023			ac_limits->ppt_pl2_sppt_def ?
1024				ac_limits->ppt_pl2_sppt_def :
1025				ac_limits->ppt_pl2_sppt_max;
1026
1027		ac_rog_tunables->ppt_pl3_fppt =
1028			ac_limits->ppt_pl3_fppt_def ?
1029				ac_limits->ppt_pl3_fppt_def :
1030				ac_limits->ppt_pl3_fppt_max;
1031
1032		ac_rog_tunables->ppt_apu_sppt =
1033			ac_limits->ppt_apu_sppt_def ?
1034				ac_limits->ppt_apu_sppt_def :
1035				ac_limits->ppt_apu_sppt_max;
1036
1037		ac_rog_tunables->ppt_platform_sppt =
1038			ac_limits->ppt_platform_sppt_def ?
1039				ac_limits->ppt_platform_sppt_def :
1040				ac_limits->ppt_platform_sppt_max;
1041
1042		ac_rog_tunables->nv_dynamic_boost =
1043			ac_limits->nv_dynamic_boost_max;
1044		ac_rog_tunables->nv_temp_target =
1045			ac_limits->nv_temp_target_max;
1046		ac_rog_tunables->nv_tgp = ac_limits->nv_tgp_max;
1047
1048		pr_debug("AC power limits initialized for %s\n", dmi_id->matches[0].substr);
1049	} else {
1050		pr_debug("No AC PPT limits defined\n");
1051	}
1052
1053	/* Initialize DC power tunables */
1054	dc_limits = power_data->dc_data;
1055	if (dc_limits) {
1056		dc_rog_tunables = kzalloc(sizeof(*asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]),
1057					  GFP_KERNEL);
1058		if (!dc_rog_tunables) {
1059			kfree(ac_rog_tunables);
1060			goto err_nomem;
1061		}
1062
1063		asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC] = dc_rog_tunables;
1064		dc_rog_tunables->power_limits = dc_limits;
1065
1066		/* Set initial DC values */
1067		dc_rog_tunables->ppt_pl1_spl =
1068			dc_limits->ppt_pl1_spl_def ?
1069				dc_limits->ppt_pl1_spl_def :
1070				dc_limits->ppt_pl1_spl_max;
1071
1072		dc_rog_tunables->ppt_pl2_sppt =
1073			dc_limits->ppt_pl2_sppt_def ?
1074				dc_limits->ppt_pl2_sppt_def :
1075				dc_limits->ppt_pl2_sppt_max;
1076
1077		dc_rog_tunables->ppt_pl3_fppt =
1078			dc_limits->ppt_pl3_fppt_def ?
1079				dc_limits->ppt_pl3_fppt_def :
1080				dc_limits->ppt_pl3_fppt_max;
1081
1082		dc_rog_tunables->ppt_apu_sppt =
1083			dc_limits->ppt_apu_sppt_def ?
1084				dc_limits->ppt_apu_sppt_def :
1085				dc_limits->ppt_apu_sppt_max;
1086
1087		dc_rog_tunables->ppt_platform_sppt =
1088			dc_limits->ppt_platform_sppt_def ?
1089				dc_limits->ppt_platform_sppt_def :
1090				dc_limits->ppt_platform_sppt_max;
1091
1092		dc_rog_tunables->nv_dynamic_boost =
1093			dc_limits->nv_dynamic_boost_max;
1094		dc_rog_tunables->nv_temp_target =
1095			dc_limits->nv_temp_target_max;
1096		dc_rog_tunables->nv_tgp = dc_limits->nv_tgp_max;
1097
1098		pr_debug("DC power limits initialized for %s\n", dmi_id->matches[0].substr);
1099	} else {
1100		pr_debug("No DC PPT limits defined\n");
1101	}
1102
1103	return;
1104
1105err_nomem:
1106	pr_err("Failed to allocate memory for tunables\n");
1107}
1108
1109static int __init asus_fw_init(void)
1110{
1111	char *wmi_uid;
1112
1113	wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
1114	if (!wmi_uid)
1115		return -ENODEV;
1116
1117	/*
1118	 * if equal to "ASUSWMI" then it's DCTS that can't be used for this
1119	 * driver, DSTS is required.
1120	 */
1121	if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI))
1122		return -ENODEV;
1123
1124	init_rog_tunables();
1125
1126	/* Must always be last step to ensure data is available */
1127	return asus_fw_attr_add();
1128}
1129
1130static void __exit asus_fw_exit(void)
1131{
1132	int i;
1133
1134	for (i = ARRAY_SIZE(armoury_attr_groups) - 1; i >= 0; i--) {
1135		if (armoury_has_devstate(armoury_attr_groups[i].wmi_devid))
1136			sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj,
1137					   armoury_attr_groups[i].attr_group);
1138	}
1139
1140	if (asus_armoury.gpu_mux_dev_id)
1141		sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &gpu_mux_mode_attr_group);
1142
1143	if (asus_armoury.mini_led_dev_id)
1144		sysfs_remove_group(&asus_armoury.fw_attr_kset->kobj, &mini_led_mode_attr_group);
1145
1146	sysfs_remove_file(&asus_armoury.fw_attr_kset->kobj, &pending_reboot.attr);
1147	kset_unregister(asus_armoury.fw_attr_kset);
1148	device_destroy(&firmware_attributes_class, MKDEV(0, 0));
1149
1150	kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_AC]);
1151	kfree(asus_armoury.rog_tunables[ASUS_ROG_TUNABLE_DC]);
1152}
1153
1154module_init(asus_fw_init);
1155module_exit(asus_fw_exit);
1156
1157MODULE_IMPORT_NS("ASUS_WMI");
1158MODULE_AUTHOR("Luke Jones <luke@ljones.dev>");
1159MODULE_DESCRIPTION("ASUS BIOS Configuration Driver");
1160MODULE_LICENSE("GPL");
1161MODULE_ALIAS("wmi:" ASUS_NB_WMI_EVENT_GUID);
~/ Read-only file preview.