* [PATCH 1/2] platform/x86: uniwill: Add MECHREVO Jiaolong 16K (GM6BG0Q)
@ 2026-10-23 3:48 dydydd
2026-10-23 3:48 ` [PATCH 2/2] platform/x86: uniwill: Add platform profile support dydydd
0 siblings, 1 reply; 2+ messages in thread
From: dydydd @ 2026-10-23 3:48 UTC (permalink / raw)
To: Armin Wolf
Cc: Hans de Goede, Ilpo Jarvinen, platform-driver-x86, linux-kernel
Add a DMI entry for the MECHREVO Jiaolong 16K, which is built by Uniwill
on the GM6BG0Q board (BIOS project ID "IDQ", EC version 1.24).
The feature set was determined by testing every sysfs attribute exposed
by the driver on the actual machine:
- fn_lock, super_key_enable and touchpad_toggle_enable are writable and
take effect
- the battery exposes charge_types (Trickle/Standard/Long_Life) instead
of charge_control_end_threshold, so BATTERY_CHARGE_MODES is used
- cpu_temp, gpu_temp, fan1_input and fan2_input report plausible values
- the keyboard backlight is RGB with four brightness levels
- ac_auto_boot and usb_powershare_high are writable
- usb_c_power_priority switches between charging and performance
- ctgp_offset is writable
This machine has no lightbar, so UNIWILL_FEATURE_LIGHTBAR is left out.
Tested on: MECHREVO Jiaolong16K Series GM6BG0Q, BIOS N.1.19MRO15,
EC 1.24, AMD Rembrandt, NVIDIA RTX 4060 Mobile.
Signed-off-by: dydydd <admin@qb.edu.kg>
---
drivers/platform/x86/uniwill/uniwill-acpi.c | 26 +++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 7a2eeaec..94ed83ed 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -2677,6 +2677,24 @@ static struct uniwill_device_descriptor machenike_l16p_descriptor __initdata = {
.kbd_led_max_brightness = 4,
};
+static struct uniwill_device_descriptor mechrevo_gm6bg0q_descriptor __initdata = {
+ .features = UNIWILL_FEATURE_FN_LOCK |
+ UNIWILL_FEATURE_SUPER_KEY |
+ UNIWILL_FEATURE_TOUCHPAD_TOGGLE |
+ UNIWILL_FEATURE_BATTERY_CHARGE_MODES |
+ UNIWILL_FEATURE_CPU_TEMP |
+ UNIWILL_FEATURE_GPU_TEMP |
+ UNIWILL_FEATURE_PRIMARY_FAN |
+ UNIWILL_FEATURE_SECONDARY_FAN |
+ UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL |
+ UNIWILL_FEATURE_USB_C_POWER_PRIORITY |
+ UNIWILL_FEATURE_KEYBOARD_BACKLIGHT |
+ UNIWILL_FEATURE_AC_AUTO_BOOT |
+ UNIWILL_FEATURE_USB_POWERSHARE,
+ .kbd_led_single_color = false,
+ .kbd_led_max_brightness = 4,
+};
+
static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = {
.features = UNIWILL_FEATURE_SUPER_KEY |
UNIWILL_FEATURE_LIGHTBAR |
@@ -2877,6 +2895,14 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = {
},
.driver_data = &machenike_l16p_descriptor,
},
+ {
+ .ident = "MECHREVO Jiaolong 16K GM6BG0Q",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MECHREVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6BG0Q"),
+ },
+ .driver_data = &mechrevo_gm6bg0q_descriptor,
+ },
{
.ident = "XMG FUSION 15 (L19)",
.matches = {
--
2.56.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH 2/2] platform/x86: uniwill: Add platform profile support
2026-10-23 3:48 [PATCH 1/2] platform/x86: uniwill: Add MECHREVO Jiaolong 16K (GM6BG0Q) dydydd
@ 2026-10-23 3:48 ` dydydd
0 siblings, 0 replies; 2+ messages in thread
From: dydydd @ 2026-10-23 3:48 UTC (permalink / raw)
To: Armin Wolf
Cc: Hans de Goede, Ilpo Jarvinen, platform-driver-x86, linux-kernel
The EC exposes PL1/PL2/PL4 registers, but the driver did not provide any
way to change them, so the admin guide stated that platform profile
support was not implemented.
Add a standard platform_profile device that writes the three power limit
registers. The values are per-machine, so they are stored in the device
descriptor instead of being hardcoded in the profile code, and machines
without known values do not register the interface at all.
The EC only latches writes to these registers while manual control (bit 0
of EC_ADDR_AP_OEM) is asserted; uniwill_ec_init() already sets that bit
for the lifetime of the driver, so the profile code re-asserts it before
writing to stay correct if the EC drops it.
After a cold boot the registers read back as 0, meaning "no limit set".
Derive the reported profile from PL1 and apply the default profile in that
case, so the sysfs value never disagrees with the hardware.
Tested on: MECHREVO Jiaolong16K Series GM6BG0Q, BIOS N.1.19MRO15,
EC 1.24.
Signed-off-by: dydydd <admin@qb.edu.kg>
---
.../admin-guide/laptops/uniwill-laptop.rst | 10 +-
drivers/platform/x86/uniwill/uniwill-acpi.c | 169 +++++++++++++++++-
2 files changed, 177 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst
index be50b45b..9442ae71 100644
--- a/Documentation/admin-guide/laptops/uniwill-laptop.rst
+++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst
@@ -38,7 +38,15 @@ two fans. Userspace applications can access sensor readings over the hwmon sysfs
Platform profile
----------------
-Support for changing the platform performance mode is currently not implemented.
+On devices that implement the EC power limit registers, the ``uniwill-laptop`` driver
+registers a standard platform profile device. It is available at
+``/sys/firmware/acpi/platform_profile`` and accepts ``low-power``, ``balanced`` and
+``performance``.
+
+Each profile maps to a set of PL1/PL2/PL4 values taken from the OEM control center
+configuration for that machine. Writing a profile updates the EC power limits, which
+the firmware keeps until the next reboot. Machines without known profile values do not
+expose the interface.
Battery Charging Control
------------------------
diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c
index 94ed83ed..6d91b8f8 100644
--- a/drivers/platform/x86/uniwill/uniwill-acpi.c
+++ b/drivers/platform/x86/uniwill/uniwill-acpi.c
@@ -39,6 +39,7 @@
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
+#include <linux/platform_profile.h>
#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/regmap.h>
@@ -369,6 +370,18 @@
#define UNIWILL_FEATURE_KEYBOARD_BACKLIGHT BIT(12)
#define UNIWILL_FEATURE_AC_AUTO_BOOT BIT(13)
#define UNIWILL_FEATURE_USB_POWERSHARE BIT(14)
+#define UNIWILL_FEATURE_PLATFORM_PROFILE BIT(15)
+
+/*
+ * Power limits used by one platform profile. The EC keeps these in single
+ * byte registers, so the values are plain watts.
+ */
+struct uniwill_performance_profile {
+ enum platform_profile_option profile;
+ u8 pl1;
+ u8 pl2;
+ u8 pl4;
+};
enum usb_c_power_priority_options {
USB_C_POWER_PRIORITY_CHARGING = 0,
@@ -418,6 +431,10 @@ struct uniwill_data {
struct notifier_block nb;
struct mutex usb_c_power_priority_lock; /* Protects dependent bit write and state safe */
enum usb_c_power_priority_options last_usb_c_power_priority_option;
+ struct mutex profile_lock; /* Protects profile writes */
+ enum platform_profile_option current_profile;
+ const struct uniwill_performance_profile *profiles;
+ unsigned int num_profiles;
};
struct uniwill_battery_entry {
@@ -430,6 +447,9 @@ struct uniwill_device_descriptor {
bool kbd_led_single_color;
u8 kbd_led_max_brightness;
u8 lightbar_max_brightness;
+ /* Only used when UNIWILL_FEATURE_PLATFORM_PROFILE is set. */
+ const struct uniwill_performance_profile *profiles;
+ unsigned int num_profiles;
/* Executed during driver probing */
int (*probe)(struct uniwill_data *data);
};
@@ -619,6 +639,9 @@ static bool uniwill_writeable_reg(struct device *dev, unsigned int reg)
case EC_ADDR_CTGP_DB_TPP_OFFSET:
case EC_ADDR_CTGP_DB_DB_OFFSET:
case EC_ADDR_USB_C_POWER_PRIORITY:
+ case EC_ADDR_PL1_SETTING:
+ case EC_ADDR_PL2_SETTING:
+ case EC_ADDR_PL4_SETTING:
return true;
default:
return false;
@@ -665,6 +688,9 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg)
case EC_ADDR_CTGP_DB_TPP_OFFSET:
case EC_ADDR_CTGP_DB_DB_OFFSET:
case EC_ADDR_USB_C_POWER_PRIORITY:
+ case EC_ADDR_PL1_SETTING:
+ case EC_ADDR_PL2_SETTING:
+ case EC_ADDR_PL4_SETTING:
return true;
default:
return false;
@@ -691,6 +717,9 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg)
case EC_ADDR_OEM_4:
case EC_ADDR_CHARGE_CTRL:
case EC_ADDR_USB_C_POWER_PRIORITY:
+ case EC_ADDR_PL1_SETTING:
+ case EC_ADDR_PL2_SETTING:
+ case EC_ADDR_PL4_SETTING:
return true;
default:
return false;
@@ -1048,6 +1077,121 @@ static int uniwill_nvidia_ctgp_init(struct uniwill_data *data)
return 0;
}
+/*
+ * Platform profile support.
+ *
+ * The EC latches PL1/PL2/PL4 only while manual control (bit 0 of
+ * EC_ADDR_AP_OEM) is asserted, otherwise it overwrites them with its own
+ * values within a few seconds. uniwill_ec_init() already sets that bit for
+ * the lifetime of the driver, so the profiles can simply write the registers.
+ */
+static const struct uniwill_performance_profile *
+uniwill_lookup_profile(struct uniwill_data *data, enum platform_profile_option profile)
+{
+ for (int i = 0; i < data->num_profiles; i++) {
+ if (data->profiles[i].profile == profile)
+ return &data->profiles[i];
+ }
+
+ return NULL;
+}
+
+static int uniwill_profile_get(struct device *dev, enum platform_profile_option *profile)
+{
+ struct uniwill_data *data = dev_get_drvdata(dev);
+
+ *profile = data->current_profile;
+
+ return 0;
+}
+
+static int uniwill_profile_set(struct device *dev, enum platform_profile_option profile)
+{
+ struct uniwill_data *data = dev_get_drvdata(dev);
+ const struct uniwill_performance_profile *limits;
+ int ret;
+
+ limits = uniwill_lookup_profile(data, profile);
+ if (!limits)
+ return -EOPNOTSUPP;
+
+ guard(mutex)(&data->profile_lock);
+
+ ret = regmap_set_bits(data->regmap, EC_ADDR_AP_OEM, ENABLE_MANUAL_CTRL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, EC_ADDR_PL1_SETTING, limits->pl1);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, EC_ADDR_PL2_SETTING, limits->pl2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, EC_ADDR_PL4_SETTING, limits->pl4);
+ if (ret < 0)
+ return ret;
+
+ data->current_profile = profile;
+
+ return 0;
+}
+
+static int uniwill_profile_probe(void *drvdata, unsigned long *choices)
+{
+ struct uniwill_data *data = drvdata;
+
+ if (!uniwill_device_supports(data, UNIWILL_FEATURE_PLATFORM_PROFILE))
+ return -ENODEV;
+
+ for (int i = 0; i < data->num_profiles; i++)
+ set_bit(data->profiles[i].profile, choices);
+
+ return 0;
+}
+
+static const struct platform_profile_ops uniwill_profile_ops = {
+ .probe = uniwill_profile_probe,
+ .profile_get = uniwill_profile_get,
+ .profile_set = uniwill_profile_set,
+};
+
+static int uniwill_profile_init(struct uniwill_data *data)
+{
+ unsigned int value;
+ int ret;
+
+ if (!uniwill_device_supports(data, UNIWILL_FEATURE_PLATFORM_PROFILE))
+ return 0;
+
+ /*
+ * The EC reports 0 after a cold boot, which means "no limit set". Derive
+ * the initial profile from PL1 so the sysfs value always matches what the
+ * firmware is actually doing.
+ */
+ ret = regmap_read(data->regmap, EC_ADDR_PL1_SETTING, &value);
+ if (ret < 0)
+ return ret;
+
+ data->current_profile = PLATFORM_PROFILE_BALANCED;
+ for (int i = 0; i < data->num_profiles; i++) {
+ if (data->profiles[i].pl1 == value) {
+ data->current_profile = data->profiles[i].profile;
+ break;
+ }
+ }
+
+ if (!value) {
+ ret = uniwill_profile_set(data->dev, data->current_profile);
+ if (ret < 0)
+ dev_warn(data->dev, "Failed to apply default profile: %d\n", ret);
+ }
+
+ return PTR_ERR_OR_ZERO(devm_platform_profile_register(data->dev, DRIVER_NAME, data,
+ &uniwill_profile_ops));
+}
+
static const char * const usb_c_power_priority_text[] = {
[USB_C_POWER_PRIORITY_CHARGING] = "charging",
[USB_C_POWER_PRIORITY_PERFORMANCE] = "performance",
@@ -2331,6 +2475,10 @@ static int uniwill_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ ret = devm_mutex_init(&pdev->dev, &data->profile_lock);
+ if (ret < 0)
+ return ret;
+
ret = uniwill_ec_init(data);
if (ret < 0)
return ret;
@@ -2339,6 +2487,8 @@ static int uniwill_probe(struct platform_device *pdev)
data->kbd_led_single_color = device_descriptor.kbd_led_single_color;
data->kbd_led_max_brightness = device_descriptor.kbd_led_max_brightness;
data->lightbar_max_brightness = device_descriptor.lightbar_max_brightness;
+ data->profiles = device_descriptor.profiles;
+ data->num_profiles = device_descriptor.num_profiles;
/*
* Some devices might need to perform some device-specific initialization steps
@@ -2371,6 +2521,10 @@ static int uniwill_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
+ ret = uniwill_profile_init(data);
+ if (ret < 0)
+ return ret;
+
ret = usb_c_power_priority_init(data);
if (ret < 0)
return ret;
@@ -2677,6 +2831,12 @@ static struct uniwill_device_descriptor machenike_l16p_descriptor __initdata = {
.kbd_led_max_brightness = 4,
};
+static const struct uniwill_performance_profile mechrevo_gm6bg0q_profiles[] = {
+ { PLATFORM_PROFILE_LOW_POWER, 35, 35, 100 },
+ { PLATFORM_PROFILE_BALANCED, 65, 65, 100 },
+ { PLATFORM_PROFILE_PERFORMANCE, 80, 80, 100 },
+};
+
static struct uniwill_device_descriptor mechrevo_gm6bg0q_descriptor __initdata = {
.features = UNIWILL_FEATURE_FN_LOCK |
UNIWILL_FEATURE_SUPER_KEY |
@@ -2690,7 +2850,14 @@ static struct uniwill_device_descriptor mechrevo_gm6bg0q_descriptor __initdata =
UNIWILL_FEATURE_USB_C_POWER_PRIORITY |
UNIWILL_FEATURE_KEYBOARD_BACKLIGHT |
UNIWILL_FEATURE_AC_AUTO_BOOT |
- UNIWILL_FEATURE_USB_POWERSHARE,
+ UNIWILL_FEATURE_USB_POWERSHARE |
+ UNIWILL_FEATURE_PLATFORM_PROFILE,
+ /*
+ * Taken from the OEM control center profiles (Mode1/2/3_Profile1.json).
+ * Low power and balanced are the "office" and "gaming" profiles.
+ */
+ .profiles = mechrevo_gm6bg0q_profiles,
+ .num_profiles = ARRAY_SIZE(mechrevo_gm6bg0q_profiles),
.kbd_led_single_color = false,
.kbd_led_max_brightness = 4,
};
--
2.56.0
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-23 3:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-23 3:48 [PATCH 1/2] platform/x86: uniwill: Add MECHREVO Jiaolong 16K (GM6BG0Q) dydydd
2026-10-23 3:48 ` [PATCH 2/2] platform/x86: uniwill: Add platform profile support dydydd
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®